File indexing completed on 2023-12-10 12:58:39
0001 /* 0002 SPDX-License-Identifier: LGPL-2.1-or-later OR MIT 0003 SPDX-FileCopyrightText: 2021 Andreas Cord-Landwehr <cordlandwehr@kde.org> 0004 */ 0005 0006 #include "journalduniquequerymodel.h" 0007 #include "journalduniquequerymodel_p.h" 0008 #include "kjournald_export.h" 0009 #include "kjournaldlib_log_general.h" 0010 #include <QDebug> 0011 #include <QDir> 0012 #include <QString> 0013 #include <memory> 0014 0015 JournaldUniqueQueryModelPrivate::~JournaldUniqueQueryModelPrivate() 0016 { 0017 sd_journal_close(mJournal); 0018 mJournal = nullptr; 0019 } 0020 0021 void JournaldUniqueQueryModelPrivate::closeJournal() 0022 { 0023 if (mJournal) { 0024 sd_journal_close(mJournal); 0025 mJournal = nullptr; 0026 } 0027 } 0028 0029 bool JournaldUniqueQueryModelPrivate::openJournal() 0030 { 0031 closeJournal(); 0032 // TODO allow custom selection of journal type 0033 int result = sd_journal_open(&mJournal, SD_JOURNAL_LOCAL_ONLY); 0034 if (result < 0) { 0035 qCCritical(KJOURNALDLIB_GENERAL) << "Could not open journal:" << strerror(-result); 0036 return false; 0037 } 0038 return true; 0039 } 0040 0041 bool JournaldUniqueQueryModelPrivate::openJournalFromPath(const QString &path) 0042 { 0043 closeJournal(); 0044 if (path.isEmpty() || !QDir().exists(path)) { 0045 qCCritical(KJOURNALDLIB_GENERAL) << "Journal directory does not exist, abort opening"; 0046 return false; 0047 } 0048 const QFileInfo fileInfo = QFileInfo(path); 0049 if (fileInfo.isDir()) { 0050 int result = sd_journal_open_directory(&mJournal, path.toStdString().c_str(), 0 /* no flags, directory defines type */); 0051 if (result < 0) { 0052 qCCritical(KJOURNALDLIB_GENERAL) << "Could not open journal:" << strerror(-result); 0053 return false; 0054 } 0055 } else if (fileInfo.isFile()) { 0056 const char **files = new const char *[1]; 0057 QByteArray journalPath = path.toLocal8Bit(); 0058 files[0] = journalPath.data(); 0059 0060 int result = sd_journal_open_files(&mJournal, files, 0 /* no flags, directory defines type */); 0061 delete[] files; 0062 if (result < 0) { 0063 qCCritical(KJOURNALDLIB_GENERAL) << "Could not open journal:" << strerror(-result); 0064 return false; 0065 } 0066 } 0067 0068 return true; 0069 } 0070 0071 void JournaldUniqueQueryModelPrivate::runQuery() 0072 { 0073 if (!mJournal || mFieldString.isEmpty()) { 0074 return; 0075 } 0076 mEntries.clear(); 0077 0078 QVector<std::pair<QString, bool>> dataList; 0079 const void *data; 0080 size_t length; 0081 int result = sd_journal_query_unique(mJournal, mFieldString.toStdString().c_str()); 0082 if (result < 0) { 0083 qCritical() << "Failed to query journal:" << strerror(-result); 0084 return; 0085 } 0086 const int fieldLength = mFieldString.length() + 1; 0087 SD_JOURNAL_FOREACH_UNIQUE(mJournal, data, length) 0088 { 0089 QString dataStr = QString::fromLocal8Bit(static_cast<const char *>(data)); 0090 dataStr = dataStr.remove(0, fieldLength); 0091 if (dataStr.endsWith(QLatin1String("\u0001"))) { 0092 dataStr = dataStr.left(dataStr.length() - QString(QLatin1String("\u0001")).length()); 0093 } 0094 if (dataStr.endsWith(QLatin1String("\u0002"))) { 0095 dataStr = dataStr.left(dataStr.length() - QString(QLatin1String("\u0002")).length()); 0096 } 0097 dataStr = JournaldHelper::cleanupString(dataStr); 0098 dataList << std::pair<QString, bool>{dataStr, true}; 0099 } 0100 0101 mEntries = dataList; 0102 } 0103 0104 JournaldUniqueQueryModel::JournaldUniqueQueryModel(QObject *parent) 0105 : QAbstractItemModel(parent) 0106 , d(new JournaldUniqueQueryModelPrivate) 0107 { 0108 d->openJournal(); 0109 d->runQuery(); 0110 } 0111 0112 JournaldUniqueQueryModel::JournaldUniqueQueryModel(const QString &journalPath, QObject *parent) 0113 : QAbstractItemModel(parent) 0114 , d(new JournaldUniqueQueryModelPrivate) 0115 { 0116 d->openJournalFromPath(journalPath); 0117 d->runQuery(); 0118 } 0119 0120 JournaldUniqueQueryModel::~JournaldUniqueQueryModel() = default; 0121 0122 bool JournaldUniqueQueryModel::setJournaldPath(const QString &path) 0123 { 0124 bool success{true}; 0125 beginResetModel(); 0126 success = d->openJournalFromPath(path); 0127 if (success) { 0128 d->runQuery(); 0129 } 0130 endResetModel(); 0131 return success; 0132 } 0133 0134 void JournaldUniqueQueryModel::setField(JournaldHelper::Field field) 0135 { 0136 setFieldString(JournaldHelper::mapField(field)); 0137 } 0138 0139 void JournaldUniqueQueryModel::setFieldString(const QString &fieldString) 0140 { 0141 beginResetModel(); 0142 d->mFieldString = fieldString; 0143 d->runQuery(); 0144 endResetModel(); 0145 } 0146 0147 QString JournaldUniqueQueryModel::fieldString() const 0148 { 0149 return d->mFieldString; 0150 } 0151 0152 QHash<int, QByteArray> JournaldUniqueQueryModel::roleNames() const 0153 { 0154 QHash<int, QByteArray> roles; 0155 roles[JournaldUniqueQueryModel::FIELD] = "field"; 0156 roles[JournaldUniqueQueryModel::SELECTED] = "selected"; 0157 return roles; 0158 } 0159 0160 void JournaldUniqueQueryModel::setSystemJournal() 0161 { 0162 beginResetModel(); 0163 d->openJournal(); 0164 endResetModel(); 0165 } 0166 0167 QModelIndex JournaldUniqueQueryModel::index(int row, int column, const QModelIndex &parent) const 0168 { 0169 return createIndex(row, column); 0170 } 0171 0172 QModelIndex JournaldUniqueQueryModel::parent(const QModelIndex &index) const 0173 { 0174 // no tree model, thus no parent 0175 return QModelIndex(); 0176 } 0177 0178 int JournaldUniqueQueryModel::rowCount(const QModelIndex &parent) const 0179 { 0180 // model represents a list and has has no children 0181 if (!parent.isValid()) { 0182 return d->mEntries.size(); 0183 } else { 0184 return 0; 0185 } 0186 } 0187 0188 int JournaldUniqueQueryModel::columnCount(const QModelIndex &parent) const 0189 { 0190 return 1; 0191 } 0192 0193 QVariant JournaldUniqueQueryModel::data(const QModelIndex &index, int role) const 0194 { 0195 if (d->mEntries.count() <= index.row()) { 0196 return QVariant(); 0197 } 0198 switch (role) { 0199 case Qt::DisplayRole: 0200 Q_FALLTHROUGH(); 0201 case JournaldUniqueQueryModel::Roles::FIELD: 0202 return d->mEntries.at(index.row()).first; 0203 case JournaldUniqueQueryModel::Roles::SELECTED: 0204 return QVariant::fromValue<bool>(d->mEntries.at(index.row()).second); 0205 } 0206 return QVariant(); 0207 } 0208 0209 bool JournaldUniqueQueryModel::setData(const QModelIndex &index, const QVariant &value, int role) 0210 { 0211 if (d->mEntries.count() <= index.row()) { 0212 return false; 0213 } 0214 if (role == JournaldUniqueQueryModel::Roles::SELECTED) { 0215 if (d->mEntries.at(index.row()).second == value.toBool()) { 0216 return false; 0217 } else { 0218 d->mEntries[index.row()].second = value.toBool(); 0219 Q_EMIT dataChanged(index, index); 0220 return true; 0221 } 0222 } 0223 return QAbstractItemModel::setData(index, value, role); 0224 }