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) ...@@ -17,6 +17,7 @@ BuildRequires: pkgconfig(mce)
BuildRequires: pkgconfig(mlite5) BuildRequires: pkgconfig(mlite5)
BuildRequires: pkgconfig(usb-moded-qt5) BuildRequires: pkgconfig(usb-moded-qt5)
BuildRequires: pkgconfig(libshadowutils) BuildRequires: pkgconfig(libshadowutils)
BuildRequires: pkgconfig(blkid)
%description %description
%{summary}. %{summary}.
......
...@@ -33,7 +33,6 @@ ...@@ -33,7 +33,6 @@
#include <QDebug> #include <QDebug>
#include <QStringList> #include <QStringList>
#include <QtSystemInfo/QStorageInfo>
#include <QNetworkInfo> #include <QNetworkInfo>
#include <QDeviceInfo> #include <QDeviceInfo>
#include <QFile> #include <QFile>
...@@ -44,9 +43,6 @@ ...@@ -44,9 +43,6 @@
#include <QVariant> #include <QVariant>
#include <QSettings> #include <QSettings>
#include <mntent.h>
namespace namespace
{ {
...@@ -119,72 +115,10 @@ void parseReleaseFile(const QString &filename, QMap<QString, QString> *result) ...@@ -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) 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)) m_devinfo(new QDeviceInfo(this))
{ {
QSettings settings(QStringLiteral("/mnt/vendor_data/vendor-data.ini"), QSettings::IniFormat); QSettings settings(QStringLiteral("/mnt/vendor_data/vendor-data.ini"), QSettings::IniFormat);
...@@ -200,12 +134,12 @@ AboutSettings::~AboutSettings() ...@@ -200,12 +134,12 @@ AboutSettings::~AboutSettings()
qlonglong AboutSettings::totalDiskSpace() const qlonglong AboutSettings::totalDiskSpace() const
{ {
return m_sysinfo->totalDiskSpace("/"); return m_partitionManager.root().bytesTotal();
} }
qlonglong AboutSettings::availableDiskSpace() const qlonglong AboutSettings::availableDiskSpace() const
{ {
return m_sysinfo->availableDiskSpace("/"); return m_partitionManager.root().bytesAvailable();
} }
QVariant AboutSettings::diskUsageModel() const QVariant AboutSettings::diskUsageModel() const
...@@ -282,98 +216,38 @@ QString AboutSettings::vendorVersion() const ...@@ -282,98 +216,38 @@ QString AboutSettings::vendorVersion() const
void AboutSettings::refreshStorageModels() 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_internalStorage.clear();
m_externalStorage.clear(); m_externalStorage.clear();
int internalPartitionCount = 0; m_partitionManager.refresh();
foreach (const StorageInfo &info, devices) {
if (!info.external) {
internalPartitionCount++;
}
}
foreach (const StorageInfo &info, devices) { for (auto partition : m_partitionManager.partitions()) {
QVariantMap row; QVariantMap row;
row[QStringLiteral("mounted")] = info.mounted; row[QStringLiteral("mounted")] = partition.status() == Partition::Mounted;
row[QStringLiteral("path")] = info.mountPath; row[QStringLiteral("path")] = partition.mountPath();
row[QStringLiteral("available")] = info.availableDiskSpace; row[QStringLiteral("available")] = partition.bytesAvailable();
row[QStringLiteral("total")] = info.totalDiskSpace; row[QStringLiteral("total")] = partition.bytesTotal();
row[QStringLiteral("filesystem")] = info.filesystem; row[QStringLiteral("filesystem")] = partition.filesystemType();
row[QStringLiteral("devicePath")] = info.devicePath; row[QStringLiteral("devicePath")] = partition.devicePath();
row[QStringLiteral("storageType")] = [&partition]() {
if (!info.external) { switch (partition.storageType()) {
row[QStringLiteral("storageType")] = internalPartitionCount == 1 ? QStringLiteral("mass") case Partition::System:
: info.mountPath == QLatin1String("/") ? QStringLiteral("system") return QStringLiteral("system");
: QStringLiteral("user"); case Partition::User:
return QStringLiteral("user");
case Partition::Mass:
m_internalStorage << QVariant(row); return QStringLiteral("mass");
} else { case Partition::External:
row[QStringLiteral("storageType")] = QStringLiteral("card"); return QStringLiteral("card");
default:
return QString();
}
}();
if (partition.storageType() == Partition::External) {
m_externalStorage << QVariant(row); m_externalStorage << QVariant(row);
} else {
m_internalStorage << QVariant(row);
} }
} }
......
...@@ -35,10 +35,12 @@ ...@@ -35,10 +35,12 @@
#include <QObject> #include <QObject>
#include <QVariant> #include <QVariant>
class QStorageInfo; #include <systemsettingsglobal.h>
#include <partitionmanager.h>
class QNetworkInfo; class QNetworkInfo;
class QDeviceInfo; class QDeviceInfo;
class AboutSettings: public QObject class SYSTEMSETTINGS_EXPORT AboutSettings: public QObject
{ {
Q_OBJECT Q_OBJECT
...@@ -90,12 +92,13 @@ signals: ...@@ -90,12 +92,13 @@ signals:
void storageChanged(); void storageChanged();
private: private:
QStorageInfo *m_sysinfo;
QNetworkInfo *m_netinfo; QNetworkInfo *m_netinfo;
QDeviceInfo *m_devinfo; QDeviceInfo *m_devinfo;
QVariantList m_internalStorage; QVariantList m_internalStorage;
QVariantList m_externalStorage; QVariantList m_externalStorage;
PartitionManager m_partitionManager;
mutable QMap<QString, QString> m_osRelease; mutable QMap<QString, QString> m_osRelease;
mutable QMap<QString, QString> m_hardwareRelease; mutable QMap<QString, QString> m_hardwareRelease;
......
...@@ -36,7 +36,9 @@ ...@@ -36,7 +36,9 @@
#include <QFileInfo> #include <QFileInfo>
#include <QJSValue> #include <QJSValue>
class AlarmToneModel #include <systemsettingsglobal.h>
class SYSTEMSETTINGS_EXPORT AlarmToneModel
: public QAbstractListModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
......
...@@ -38,7 +38,9 @@ ...@@ -38,7 +38,9 @@
#include <timed-qt5/interface> #include <timed-qt5/interface>
#include <timed-qt5/wallclock> #include <timed-qt5/wallclock>
class DateTimeSettings: public QObject #include <systemsettingsglobal.h>
class SYSTEMSETTINGS_EXPORT DateTimeSettings: public QObject
{ {
Q_OBJECT Q_OBJECT
......
...@@ -40,9 +40,11 @@ ...@@ -40,9 +40,11 @@
#include <QDBusInterface> #include <QDBusInterface>
#include <QNetworkInterface> #include <QNetworkInterface>
#include <systemsettingsglobal.h>
class DeveloperModeSettingsWorker; class DeveloperModeSettingsWorker;
class DeveloperModeSettings : public QObject class SYSTEMSETTINGS_EXPORT DeveloperModeSettings : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_ENUMS(Status) Q_ENUMS(Status)
......
...@@ -36,7 +36,9 @@ ...@@ -36,7 +36,9 @@
#include <QProcess> #include <QProcess>
#include <QDebug> #include <QDebug>
class DeviceLockInterface : public QObject #include <systemsettingsglobal.h>
class SYSTEMSETTINGS_EXPORT DeviceLockInterface : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_ENUMS(ResetMode) Q_ENUMS(ResetMode)
......
...@@ -38,9 +38,11 @@ ...@@ -38,9 +38,11 @@
#include <QJSValue> #include <QJSValue>
#include <QScopedPointer> #include <QScopedPointer>
#include <systemsettingsglobal.h>
class DiskUsagePrivate; class DiskUsagePrivate;
class DiskUsage : public QObject class SYSTEMSETTINGS_EXPORT DiskUsage : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_DECLARE_PRIVATE(DiskUsage) Q_DECLARE_PRIVATE(DiskUsage)
......
...@@ -40,7 +40,9 @@ class ComNokiaMceSignalInterface; ...@@ -40,7 +40,9 @@ class ComNokiaMceSignalInterface;
class QDBusVariant; class QDBusVariant;
class MGConfItem; class MGConfItem;
class DisplaySettings: public QObject #include <systemsettingsglobal.h>
class SYSTEMSETTINGS_EXPORT DisplaySettings: public QObject
{ {
Q_OBJECT Q_OBJECT
......
...@@ -35,7 +35,10 @@ ...@@ -35,7 +35,10 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QList> #include <QList>
class Language {
#include <systemsettingsglobal.h>
class SYSTEMSETTINGS_EXPORT Language {
public: public:
Language(QString name, QString localeCode, QString region, QString regionLabel); Language(QString name, QString localeCode, QString region, QString regionLabel);
QString name() const; QString name() const;
...@@ -50,7 +53,7 @@ private: ...@@ -50,7 +53,7 @@ private:
QString m_regionLabel; QString m_regionLabel;
}; };
class LanguageModel: public QAbstractListModel class SYSTEMSETTINGS_EXPORT LanguageModel: public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int currentIndex READ currentIndex NOTIFY currentIndexChanged) 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();