Commit 4270bf6c authored by Andrew den Exter's avatar Andrew den Exter

[settings] Add C++ API for querying partition information. Contributes to JB#35430

parent 110fd5bf
......@@ -17,6 +17,7 @@ BuildRequires: pkgconfig(mce)
BuildRequires: pkgconfig(mlite5)
BuildRequires: pkgconfig(usb-moded-qt5)
BuildRequires: pkgconfig(libshadowutils)
BuildRequires: pkgconfig(blkid)
%description
%{summary}.
......
......@@ -33,7 +33,6 @@
#include <QDebug>
#include <QStringList>
#include <QtSystemInfo/QStorageInfo>
#include <QNetworkInfo>
#include <QDeviceInfo>
#include <QFile>
......@@ -44,9 +43,6 @@
#include <QVariant>
#include <QSettings>
#include <mntent.h>
namespace
{
......@@ -119,72 +115,10 @@ void parseReleaseFile(const QString &filename, QMap<QString, QString> *result)
}
}
struct StorageInfo {
StorageInfo()
: partitionSize(0)
, availableDiskSpace(0)
, totalDiskSpace(0)
, external(false)
, mounted(false)
{ }
QString mountPath;
QString devicePath;
QString filesystem;
quint64 partitionSize;
qlonglong availableDiskSpace;
qlonglong totalDiskSpace;
bool external;
bool mounted;
};
QMap<QString, StorageInfo> parseExternalPartitions()
{
QMap<QString, StorageInfo> devices;
QFile partitions(QStringLiteral("/proc/partitions"));
if (!partitions.open(QIODevice::ReadOnly))
return devices;
// Read file headers
partitions.readLine();
partitions.readLine();
while (!partitions.atEnd()) {
QByteArray line = partitions.readLine().trimmed();
int nameIndex = line.lastIndexOf(' ');
if (nameIndex <= 0)
continue;
int sizeIndex = line.lastIndexOf(' ', nameIndex - 1);
if (sizeIndex == -1)
continue;
QByteArray size = line.mid(sizeIndex+1, nameIndex - sizeIndex - 1);
QByteArray name = line.mid(nameIndex+1);
if (name.startsWith("mmcblk1")) {
// If adding a partition remove the whole device.
devices.remove(QStringLiteral("/dev/mmcblk1"));
StorageInfo info;
info.devicePath = QStringLiteral("/dev/") + QString::fromLatin1(name);
info.partitionSize = size.toULongLong() * 1024;
info.external = true;
devices.insert(info.devicePath, info);
}
}
return devices;
}
}
AboutSettings::AboutSettings(QObject *parent)
: QObject(parent), m_sysinfo(new QStorageInfo(this)), m_netinfo(new QNetworkInfo(this)),
: QObject(parent), m_netinfo(new QNetworkInfo(this)),
m_devinfo(new QDeviceInfo(this))
{
QSettings settings(QStringLiteral("/mnt/vendor_data/vendor-data.ini"), QSettings::IniFormat);
......@@ -200,12 +134,12 @@ AboutSettings::~AboutSettings()
qlonglong AboutSettings::totalDiskSpace() const
{
return m_sysinfo->totalDiskSpace("/");
return m_partitionManager.root().bytesTotal();
}
qlonglong AboutSettings::availableDiskSpace() const
{
return m_sysinfo->availableDiskSpace("/");
return m_partitionManager.root().bytesAvailable();
}
QVariant AboutSettings::diskUsageModel() const
......@@ -282,98 +216,38 @@ QString AboutSettings::vendorVersion() const
void AboutSettings::refreshStorageModels()
{
// Optional mountpoints that we want to report disk usage for
QStringList candidates;
candidates << QStringLiteral("/home");
QMap<QString, StorageInfo> devices = parseExternalPartitions();
FILE *fsd = setmntent(_PATH_MOUNTED, "r");
struct mntent entry;
char buffer[3*PATH_MAX];
while ((getmntent_r(fsd, &entry, buffer, sizeof(buffer))) != NULL) {
StorageInfo info;
info.mountPath = QString::fromLatin1(entry.mnt_dir);
info.devicePath = QString::fromLatin1(entry.mnt_fsname);
info.filesystem = QString::fromLatin1(entry.mnt_type);
bool addInfo = false;
if (info.mountPath == QLatin1String("/") && info.devicePath.startsWith(QLatin1Char('/'))) {
// Always report rootfs, replacing other mounts from same device
addInfo = true;
} else if (!devices.contains(info.devicePath) || devices.value(info.devicePath).external) {
// Optional candidates and external storage
if (candidates.contains(info.mountPath)) {
addInfo = true;
} else if (info.devicePath.startsWith(QLatin1String("/dev/mmcblk1"))) {
info.external = true;
addInfo = true;
}
}
if (addInfo) {
info.mounted = !info.external || !info.mountPath.isEmpty();
info.availableDiskSpace = info.mounted ? m_sysinfo->availableDiskSpace(info.mountPath) : 0;
info.totalDiskSpace = info.mounted ? m_sysinfo->totalDiskSpace(info.mountPath) : info.partitionSize;
bool ignoreDuplicateEntry = false;
if (info.external) {
for (QMap<QString, StorageInfo>::Iterator it = devices.begin(); it != devices.end(); it++) {
const StorageInfo &currInfo = it.value();
if (info.external && info.totalDiskSpace == currInfo.totalDiskSpace) {
const QString &currMountPath = currInfo.mountPath;
// it appears the same device has been mounted under multiple paths, so ignore
// the one with the longer device path, assuming it's the extraneous entry
if (currMountPath.indexOf(info.mountPath) >= 0) {
// remove the duplicate and keep this entry
devices.erase(it);
break;
} else if (info.mountPath.indexOf(currMountPath) >= 0) {
// ignore this entry
ignoreDuplicateEntry = true;
break;
}
}
}
}
if (!ignoreDuplicateEntry) {
devices.insert(info.devicePath, info);
}
}
}
endmntent(fsd);
m_internalStorage.clear();
m_externalStorage.clear();
int internalPartitionCount = 0;
foreach (const StorageInfo &info, devices) {
if (!info.external) {
internalPartitionCount++;
}
}
m_partitionManager.refresh();
foreach (const StorageInfo &info, devices) {
for (auto partition : m_partitionManager.partitions()) {
QVariantMap row;
row[QStringLiteral("mounted")] = info.mounted;
row[QStringLiteral("path")] = info.mountPath;
row[QStringLiteral("available")] = info.availableDiskSpace;
row[QStringLiteral("total")] = info.totalDiskSpace;
row[QStringLiteral("filesystem")] = info.filesystem;
row[QStringLiteral("devicePath")] = info.devicePath;
if (!info.external) {
row[QStringLiteral("storageType")] = internalPartitionCount == 1 ? QStringLiteral("mass")
: info.mountPath == QLatin1String("/") ? QStringLiteral("system")
: QStringLiteral("user");
m_internalStorage << QVariant(row);
} else {
row[QStringLiteral("storageType")] = QStringLiteral("card");
row[QStringLiteral("mounted")] = partition.status() == Partition::Mounted;
row[QStringLiteral("path")] = partition.mountPath();
row[QStringLiteral("available")] = partition.bytesAvailable();
row[QStringLiteral("total")] = partition.bytesTotal();
row[QStringLiteral("filesystem")] = partition.filesystemType();
row[QStringLiteral("devicePath")] = partition.devicePath();
row[QStringLiteral("storageType")] = [&partition]() {
switch (partition.storageType()) {
case Partition::System:
return QStringLiteral("system");
case Partition::User:
return QStringLiteral("user");
case Partition::Mass:
return QStringLiteral("mass");
case Partition::External:
return QStringLiteral("card");
default:
return QString();
}
}();
if (partition.storageType() == Partition::External) {
m_externalStorage << QVariant(row);
} else {
m_internalStorage << QVariant(row);
}
}
......
......@@ -35,10 +35,12 @@
#include <QObject>
#include <QVariant>
class QStorageInfo;
#include <systemsettingsglobal.h>
#include <partitionmanager.h>
class QNetworkInfo;
class QDeviceInfo;
class AboutSettings: public QObject
class SYSTEMSETTINGS_EXPORT AboutSettings: public QObject
{
Q_OBJECT
......@@ -90,12 +92,13 @@ signals:
void storageChanged();
private:
QStorageInfo *m_sysinfo;
QNetworkInfo *m_netinfo;
QDeviceInfo *m_devinfo;
QVariantList m_internalStorage;
QVariantList m_externalStorage;
PartitionManager m_partitionManager;
mutable QMap<QString, QString> m_osRelease;
mutable QMap<QString, QString> m_hardwareRelease;
......
......@@ -36,7 +36,9 @@
#include <QFileInfo>
#include <QJSValue>
class AlarmToneModel
#include <systemsettingsglobal.h>
class SYSTEMSETTINGS_EXPORT AlarmToneModel
: public QAbstractListModel
{
Q_OBJECT
......
......@@ -38,7 +38,9 @@
#include <timed-qt5/interface>
#include <timed-qt5/wallclock>
class DateTimeSettings: public QObject
#include <systemsettingsglobal.h>
class SYSTEMSETTINGS_EXPORT DateTimeSettings: public QObject
{
Q_OBJECT
......
......@@ -40,9 +40,11 @@
#include <QDBusInterface>
#include <QNetworkInterface>
#include <systemsettingsglobal.h>
class DeveloperModeSettingsWorker;
class DeveloperModeSettings : public QObject
class SYSTEMSETTINGS_EXPORT DeveloperModeSettings : public QObject
{
Q_OBJECT
Q_ENUMS(Status)
......
......@@ -36,7 +36,9 @@
#include <QProcess>
#include <QDebug>
class DeviceLockInterface : public QObject
#include <systemsettingsglobal.h>
class SYSTEMSETTINGS_EXPORT DeviceLockInterface : public QObject
{
Q_OBJECT
Q_ENUMS(ResetMode)
......
......@@ -38,9 +38,11 @@
#include <QJSValue>
#include <QScopedPointer>
#include <systemsettingsglobal.h>
class DiskUsagePrivate;
class DiskUsage : public QObject
class SYSTEMSETTINGS_EXPORT DiskUsage : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(DiskUsage)
......
......@@ -40,7 +40,9 @@ class ComNokiaMceSignalInterface;
class QDBusVariant;
class MGConfItem;
class DisplaySettings: public QObject
#include <systemsettingsglobal.h>
class SYSTEMSETTINGS_EXPORT DisplaySettings: public QObject
{
Q_OBJECT
......
......@@ -35,7 +35,10 @@
#include <QAbstractListModel>
#include <QList>
class Language {
#include <systemsettingsglobal.h>
class SYSTEMSETTINGS_EXPORT Language {
public:
Language(QString name, QString localeCode, QString region, QString regionLabel);
QString name() const;
......@@ -50,7 +53,7 @@ private:
QString m_regionLabel;
};
class LanguageModel: public QAbstractListModel
class SYSTEMSETTINGS_EXPORT LanguageModel: public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int currentIndex READ currentIndex NOTIFY currentIndexChanged)
......
/*
* Copyright (C) 2016 Jolla Ltd. <andrew.den.exter@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* "Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
*/
#include "partition_p.h"
#include "partitionmanager_p.h"
static const auto systemdService = QStringLiteral("org.freedesktop.systemd1");
static const auto systemdPath = QStringLiteral("/org/freedesktop/systemd1");
static const auto propertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties");
static const auto unitInterface = QStringLiteral("org.freedesktop.systemd1.Unit");
static const auto managerInterface = QStringLiteral("org.freedesktop.systemd1.Manager");
static const auto activeStateProperty = QStringLiteral("ActiveState");
void PartitionPrivate::getUnit()
{
if (storageType != Partition::External) {
return;
}
const QString serviceName = QStringLiteral("mount-sd@") + deviceName + QStringLiteral(".service");
Q_ASSERT(!pendingCall);
Q_ASSERT(unitPath.isEmpty());
pendingCall = DBus::call<QDBusObjectPath, QString>(
this,
systemdPath,
managerInterface,
QStringLiteral("GetUnit"),
serviceName,
[this](const QDBusObjectPath &path) {
pendingCall = nullptr;
setUnitPath(path.path());
}, [this](const QDBusError &error) {
pendingCall = nullptr;
qWarning("Failed to query the unit path of %s: %s", qPrintable(devicePath), qPrintable(error.message()));
});
}
void PartitionPrivate::setUnitPath(const QString &path)
{
unitPath = path;
QDBusConnection systemBus = QDBusConnection::systemBus();
if (!systemBus.connect(
systemdService,
unitPath,
propertiesInterface,
QStringLiteral("PropertiesChanged"),
this,
SLOT(propertiesChanged(QString,QVariantMap,QStringList)))) {
qWarning("Failed to connect to unit properties changed signal: %s", qPrintable(systemBus.lastError().message()));
}
updateState();
}
void PartitionPrivate::updateState()
{
if (unitPath.isEmpty()) {
return;
}
delete pendingCall;
pendingCall = DBus::call<QVariant, QString, QString>(
this,
unitPath,
propertiesInterface,
QStringLiteral("Get"),
unitInterface,
activeStateProperty,
[this](const QVariant &state) {
pendingCall = nullptr;
activeState = state.toString();
mountFailed = activeState == QStringLiteral("failed");
if (manager) {
manager->refresh(this);
}
}, [this](const QDBusError &error) {
pendingCall = nullptr;
qWarning("Failed to query the active state of %s: %s", qPrintable(devicePath), qPrintable(error.message()));
});
}
void PartitionPrivate::propertiesChanged(
const QString &, const QVariantMap &, const QStringList &invalidated)
{
if (invalidated.contains(activeStateProperty)) {
updateState();
}
}
Partition::Partition()
{
}
Partition::Partition(const Partition &partition)
: d(partition.d)
{
}
Partition::Partition(const QExplicitlySharedDataPointer<PartitionPrivate> &d)
: d(d)
{
}
Partition &Partition::operator =(const Partition &partition)
{
d = partition.d;
return *this;
}
Partition::~Partition()
{
}
bool Partition::operator ==(const Partition &partition) const
{
return d == partition.d;
}
bool Partition::operator !=(const Partition &partition) const
{
return d != partition.d;
}
bool Partition::isReadOnly() const
{
return !d || d->readOnly;
}
Partition::Status Partition::status() const
{
return d ? d->status : Partition::Unmounted;
}
bool Partition::canMount() const
{
return d && d->canMount;
}
bool Partition::mountFailed() const
{
return d && d->mountFailed;
}
Partition::StorageType Partition::storageType() const
{
return d ? d->storageType : Invalid;
}
QString Partition::devicePath() const
{
return d ? d->devicePath : QString();
}
QString Partition::mountPath() const
{
return d ? d->mountPath : QString();
}
QString Partition::filesystemType() const
{
return d ? d->filesystemType : QString();
}
qint64 Partition::bytesAvailable() const
{
return d ? d->bytesAvailable : 0;
}
qint64 Partition::bytesTotal() const
{
return d ? d->bytesTotal : 0;
}
qint64 Partition::bytesFree() const
{
return d ? d->bytesFree : 0;
}
void Partition::refresh()
{
if (const auto manager = d ? d->manager : nullptr) {
manager->refresh(d.data());
emit manager->partitionChanged(*this);
}
}
/*
* Copyright (C) 2016 Jolla Ltd. <andrew.den.exter@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* "Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
*/
#ifndef PARTITION_H
#define PARTITION_H
#include <QSharedData>
#include <systemsettingsglobal.h>
class PartitionPrivate;
class SYSTEMSETTINGS_EXPORT Partition
{
public:
enum StorageType
{
Invalid = 0x00,
System = 0x01,
User = 0x02,
Mass = 0x04,
External = 0x08,
ExcludeParents = 0x1000,
Internal = System | User | Mass,
Any = System | User | Mass | External
};
enum Status {
Unmounted,
Mounting,
Mounted,
Unmounting
};
Q_DECLARE_FLAGS(StorageTypes, StorageType)
Partition();
Partition(const Partition &partition);
Partition &operator =(const Partition &partition);
~Partition();
bool operator ==(const Partition &partition) const;
bool operator !=(const Partition &partition) const;
bool isReadOnly() const;
Status status() const;
bool canMount() const;
bool mountFailed() const;
StorageType storageType() const;
QString devicePath() const;
QString mountPath() const;
QString filesystemType() const;
qint64 bytesAvailable() const;
qint64 bytesTotal() const;
qint64 bytesFree() const;
void refresh();
private:
friend class PartitionManagerPrivate;
explicit Partition(const QExplicitlySharedDataPointer<PartitionPrivate> &d);
QExplicitlySharedDataPointer<PartitionPrivate> d;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Partition::StorageTypes)
#endif
/*
* Copyright (C) 2016 Jolla Ltd. <andrew.den.exter@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* "Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
*/
#ifndef PARTITION_P_H
#define PARTITION_P_H
#include "partition.h"
#include <QDBusConnection>
#include <QDBusPendingCallWatcher>
#include <QDBusPendingReply>
class PartitionManagerPrivate;
class PartitionPrivate : public QObject, public QSharedData
{
Q_OBJECT
public:
PartitionPrivate(PartitionManagerPrivate *manager)
: manager(manager)
, pendingCall(nullptr)