File indexing completed on 2024-04-21 05:47:46
0001 /******************************************************************************* 0002 * Copyright (C) 2016 Ragnar Thomsen <rthomsen6@gmail.com> * 0003 * * 0004 * This program is free software: you can redistribute it and/or modify it * 0005 * under the terms of the GNU General Public License as published by the Free * 0006 * Software Foundation, either version 2 of the License, or (at your option) * 0007 * any later version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT * 0010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * 0011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 0012 * more details. * 0013 * * 0014 * You should have received a copy of the GNU General Public License along * 0015 * with this program. If not, see <http://www.gnu.org/licenses/>. * 0016 *******************************************************************************/ 0017 0018 #include "mainwindow.h" 0019 0020 #include <KActionCollection> 0021 #include <KAuth/Action> 0022 #include <KAuth/ExecuteJob> 0023 #include <KColorScheme> 0024 #include <KLocalizedString> 0025 #include <KMessageBox> 0026 #include <KXMLGUIFactory> 0027 0028 #include <QDebug> 0029 #include <QPlainTextEdit> 0030 #include <QProcess> 0031 #include <QPushButton> 0032 #include <QRegularExpression> 0033 #include <QtDBus> 0034 0035 #include <unistd.h> 0036 0037 MainWindow::MainWindow(QWidget *parent) 0038 : KXmlGuiWindow(parent) 0039 { 0040 ui.setupUi(this); 0041 0042 ui.leSearchUnit->setFocus(); 0043 0044 // See if systemd is reachable via dbus 0045 if (getDbusProperty(QStringLiteral("Version"), sysdMgr) != QLatin1String("invalidIface")) { 0046 systemdVersion = getDbusProperty(QStringLiteral("Version"), sysdMgr).toString().remove(QStringLiteral("systemd ")).toInt(); 0047 qDebug() << "Detected systemd" << systemdVersion; 0048 } else { 0049 qDebug() << "Unable to contact systemd daemon!"; 0050 KMessageBox::error(this, i18n("Unable to contact the systemd daemon. Quitting..."), i18n("SystemdGenie")); 0051 close(); 0052 } 0053 0054 // Search for user dbus. 0055 if (QFileInfo::exists(QStringLiteral("/run/user/%1/bus").arg(QString::number(getuid())))) { 0056 m_userBusPath = QStringLiteral("unix:path=/run/user/%1/bus").arg(QString::number(getuid())); 0057 } else if (QFileInfo::exists(QStringLiteral("/run/user/%1/dbus/user_bus_socket").arg(QString::number(getuid())))) { 0058 m_userBusPath = QStringLiteral("unix:path=/run/user/%1/dbus/user_bus_socket").arg(QString::number(getuid())); 0059 } else { 0060 qDebug() << "User dbus not found. Support for user units disabled."; 0061 ui.tabWidget->setTabEnabled(1, false); 0062 ui.tabWidget->setTabToolTip(1, i18n("User dbus not found. Support for user units disabled.")); 0063 enableUserUnits = false; 0064 } 0065 0066 QStringList allowUnitTypes = QStringList{i18n("All"), i18n("Services"), i18n("Automounts"), i18n("Devices"), 0067 i18n("Mounts"), i18n("Paths"), i18n("Scopes"), i18n("Slices"), 0068 i18n("Sockets"), i18n("Swaps"), i18n("Targets"), i18n("Timers")}; 0069 ui.cmbUnitTypes->addItems(allowUnitTypes); 0070 ui.cmbUserUnitTypes->addItems(allowUnitTypes); 0071 0072 // Get list of units 0073 slotRefreshUnitsList(true, sys); 0074 slotRefreshUnitsList(true, user); 0075 0076 setupUnitslist(); 0077 setupSessionlist(); 0078 setupTimerlist(); 0079 setupConfFilelist(); 0080 setupActions(); 0081 0082 setupSignalSlots(); 0083 0084 m_lblLog = new QLabel(this); 0085 m_lblLog->setText(QString()); 0086 statusBar()->addPermanentWidget(m_lblLog, 20); 0087 0088 setupGUI(Default, QStringLiteral("systemdgenieui.rc")); 0089 } 0090 0091 MainWindow::~MainWindow() 0092 { 0093 } 0094 0095 void MainWindow::quit() 0096 { 0097 close(); 0098 } 0099 0100 0101 QDBusArgument &operator<<(QDBusArgument &argument, const SystemdUnit &unit) 0102 { 0103 argument.beginStructure(); 0104 argument << unit.id 0105 << unit.description 0106 << unit.load_state 0107 << unit.active_state 0108 << unit.sub_state 0109 << unit.following 0110 << unit.unit_path 0111 << unit.job_id 0112 << unit.job_type 0113 << unit.job_path; 0114 argument.endStructure(); 0115 return argument; 0116 } 0117 0118 const QDBusArgument &operator>>(const QDBusArgument &argument, SystemdUnit &unit) 0119 { 0120 argument.beginStructure(); 0121 argument >> unit.id 0122 >> unit.description 0123 >> unit.load_state 0124 >> unit.active_state 0125 >> unit.sub_state 0126 >> unit.following 0127 >> unit.unit_path 0128 >> unit.job_id 0129 >> unit.job_type 0130 >> unit.job_path; 0131 argument.endStructure(); 0132 return argument; 0133 } 0134 0135 QDBusArgument &operator<<(QDBusArgument &argument, const SystemdSession &session) 0136 { 0137 argument.beginStructure(); 0138 argument << session.session_id 0139 << session.user_id 0140 << session.user_name 0141 << session.seat_id 0142 << session.session_path; 0143 argument.endStructure(); 0144 return argument; 0145 } 0146 0147 const QDBusArgument &operator>>(const QDBusArgument &argument, SystemdSession &session) 0148 { 0149 argument.beginStructure(); 0150 argument >> session.session_id 0151 >> session.user_id 0152 >> session.user_name 0153 >> session.seat_id 0154 >> session.session_path; 0155 argument.endStructure(); 0156 return argument; 0157 } 0158 0159 void MainWindow::setupSignalSlots() 0160 { 0161 // Connect signals for unit tabs. 0162 connect(ui.chkInactiveUnits, &QCheckBox::stateChanged, this, &MainWindow::slotChkShowUnits); 0163 connect(ui.chkUnloadedUnits, &QCheckBox::stateChanged, this, &MainWindow::slotChkShowUnits); 0164 connect(ui.chkInactiveUserUnits, &QCheckBox::stateChanged, this, &MainWindow::slotChkShowUnits); 0165 connect(ui.chkUnloadedUserUnits, &QCheckBox::stateChanged, this, &MainWindow::slotChkShowUnits); 0166 connect(ui.cmbUnitTypes, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &MainWindow::slotCmbUnitTypes); 0167 connect(ui.cmbUserUnitTypes,static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &MainWindow::slotCmbUnitTypes); 0168 connect(ui.tblUnits, &QTableView::customContextMenuRequested, this, &MainWindow::slotUnitContextMenu); 0169 connect(ui.tblUserUnits, &QTableView::customContextMenuRequested, this, &MainWindow::slotUnitContextMenu); 0170 connect(ui.tblConfFiles, &QTableView::customContextMenuRequested, this, &MainWindow::slotConfFileContextMenu); 0171 connect(ui.leSearchUnit, &QLineEdit::textChanged, this, &MainWindow::slotLeSearchUnitChanged); 0172 connect(ui.leSearchUserUnit, &QLineEdit::textChanged, this, &MainWindow::slotLeSearchUnitChanged); 0173 connect(ui.tblUnits->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::updateActions); 0174 connect(ui.tblUserUnits->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::updateActions); 0175 connect(ui.tblConfFiles->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::updateActions); 0176 connect(ui.tblSessions->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::updateActions); 0177 connect(ui.tabWidget, &QTabWidget::currentChanged, this, &MainWindow::updateActions); 0178 0179 connect(ui.tabWidget, &QTabWidget::currentChanged, [=]{ 0180 if (ui.tabWidget->currentIndex() == 0) 0181 ui.leSearchUnit->setFocus(); 0182 else if (ui.tabWidget->currentIndex() == 1) 0183 ui.leSearchUserUnit->setFocus(); 0184 }); 0185 0186 // Connect signals for sessions tab. 0187 connect(ui.tblSessions, &QTableView::customContextMenuRequested, this, &MainWindow::slotSessionContextMenu); 0188 0189 // Subscribe to dbus signals from systemd system daemon and connect them to slots. 0190 callDbusMethod(QStringLiteral("Subscribe"), sysdMgr); 0191 m_systemBus.connect(connSystemd, 0192 pathSysdMgr, 0193 ifaceMgr, 0194 QStringLiteral("Reloading"), 0195 this, 0196 SLOT(slotSystemSystemdReloading(bool))); 0197 /*m_systemBus.connect(connSystemd, 0198 pathSysdMgr, 0199 ifaceMgr, 0200 QStringLiteral("UnitNew"), 0201 this, 0202 SLOT(slotSystemUnitNew(const QString&, const QDBusObjectPath&))); 0203 m_systemBus.connect(connSystemd, 0204 pathSysdMgr, 0205 ifaceMgr, 0206 QStringLiteral("UnitRemoved"), 0207 this, 0208 SLOT(slotSystemUnitRemoved(const QString&, const QDBusObjectPath&)));*/ 0209 0210 m_systemBus.connect(connSystemd, 0211 pathSysdMgr, 0212 ifaceMgr, 0213 QStringLiteral("JobNew"), 0214 this, 0215 SLOT(slotSystemJobNew(uint, const QDBusObjectPath&, const QString&))); 0216 // We need to use the JobRemoved signal, because stopping units does not emit PropertiesChanged signal 0217 m_systemBus.connect(connSystemd, 0218 pathSysdMgr, 0219 ifaceMgr, 0220 QStringLiteral("JobRemoved"), 0221 this, 0222 SLOT(slotSystemJobRemoved(uint, const QDBusObjectPath&, const QString&, const QString&))); 0223 0224 m_systemBus.connect(connSystemd, 0225 pathSysdMgr, 0226 ifaceMgr, 0227 QStringLiteral("UnitFilesChanged"), 0228 this, 0229 SLOT(slotSystemUnitFilesChanged())); 0230 m_systemBus.connect(connSystemd, 0231 QString(), 0232 ifaceDbusProp, 0233 QStringLiteral("PropertiesChanged"), 0234 this, 0235 SLOT(slotSystemPropertiesChanged(const QString&, const QVariantMap&, const QStringList&))); 0236 0237 0238 // Subscribe to dbus signals from systemd user daemon and connect them to slots 0239 callDbusMethod(QStringLiteral("Subscribe"), sysdMgr, user); 0240 QDBusConnection userbus = QDBusConnection::connectToBus(m_userBusPath, connSystemd); 0241 userbus.connect(connSystemd, 0242 pathSysdMgr, 0243 ifaceMgr, 0244 QStringLiteral("Reloading"), 0245 this, 0246 SLOT(slotUserSystemdReloading(bool))); 0247 /*userbus.connect(connSystemd, 0248 pathSysdMgr, 0249 ifaceMgr, 0250 QStringLiteral("UnitNew"), 0251 this, 0252 SLOT(slotUnitNew(const QString&, const QDBusObjectPath&))); 0253 userbus.connect(connSystemd, 0254 pathSysdMgr, 0255 ifaceMgr, 0256 QStringLiteral("UnitRemoved"), 0257 this, 0258 SLOT(slotUnitRemoved(const QString&, const QDBusObjectPath&)));*/ 0259 0260 userbus.connect(connSystemd, 0261 pathSysdMgr, 0262 ifaceMgr, 0263 QStringLiteral("JobNew"), 0264 this, 0265 SLOT(slotUserJobNew(uint, const QDBusObjectPath&, const QString&))); 0266 // We need to use the JobRemoved signal, because stopping units does not emit PropertiesChanged signal 0267 userbus.connect(connSystemd, 0268 pathSysdMgr, 0269 ifaceMgr, 0270 QStringLiteral("JobRemoved"), 0271 this, 0272 SLOT(slotUserJobRemoved(uint, const QDBusObjectPath&, const QString&, const QString&))); 0273 0274 userbus.connect(connSystemd, 0275 pathSysdMgr, 0276 ifaceMgr, 0277 QStringLiteral("UnitFilesChanged"), 0278 this, 0279 SLOT(slotUserUnitFilesChanged())); 0280 userbus.connect(connSystemd, 0281 QString(), 0282 ifaceDbusProp, 0283 QStringLiteral("PropertiesChanged"), 0284 this, 0285 SLOT(slotUserPropertiesChanged(const QString&, const QVariantMap&, const QStringList&))); 0286 0287 /* 0288 userbus.connect(connSystemd, 0289 pathSysdMgr, 0290 ifaceMgr, 0291 QStringLiteral("Reloading"), 0292 this, 0293 SLOT(slotUserSystemdReloading(bool))); 0294 userbus.connect(connSystemd, 0295 pathSysdMgr, 0296 ifaceMgr, 0297 QStringLiteral("UnitFilesChanged"), 0298 this, 0299 SLOT(slotUserUnitsChanged())); 0300 userbus.connect(connSystemd, QString(), ifaceDbusProp, 0301 QStringLiteral("PropertiesChanged"), this, SLOT(slotUserUnitsChanged())); 0302 userbus.connect(connSystemd, pathSysdMgr, ifaceMgr, 0303 QStringLiteral("JobRemoved"), this, SLOT(slotUserUnitsChanged())); */ 0304 0305 // logind. 0306 m_systemBus.connect(connLogind, 0307 QString(), 0308 ifaceDbusProp, 0309 QStringLiteral("PropertiesChanged"), 0310 this, 0311 SLOT(slotLogindPropertiesChanged(QString,QVariantMap,QStringList))); 0312 } 0313 0314 0315 void MainWindow::setupUnitslist() 0316 { 0317 // Sets up the units list initially 0318 0319 // Register the meta type for storing units 0320 qDBusRegisterMetaType<SystemdUnit>(); 0321 0322 QMap<filterType, QString> filters; 0323 filters[activeState] = QString(); 0324 filters[unitType] = QString(); 0325 filters[unitName] = QString(); 0326 0327 // QList<SystemdUnit> *ptrUnits; 0328 // ptrUnits = &m_systemUnitsList; 0329 0330 // Setup the system unit model 0331 ui.tblUnits->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter); 0332 m_systemUnitModel = new UnitModel(this, &m_systemUnitsList); 0333 m_systemUnitFilterModel = new SortFilterUnitModel(this); 0334 m_systemUnitFilterModel->setDynamicSortFilter(false); 0335 m_systemUnitFilterModel->initFilterMap(filters); 0336 m_systemUnitFilterModel->setSourceModel(m_systemUnitModel); 0337 ui.tblUnits->setModel(m_systemUnitFilterModel); 0338 ui.tblUnits->sortByColumn(0, Qt::AscendingOrder); 0339 ui.tblUnits->resizeColumnsToContents(); 0340 ui.tblUnits->setColumnWidth(0, 400); 0341 ui.tblUnits->resizeRowsToContents(); 0342 0343 // Setup the user unit model 0344 ui.tblUserUnits->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter); 0345 m_userUnitModel = new UnitModel(this, &m_userUnitsList, m_userBusPath); 0346 m_userUnitFilterModel = new SortFilterUnitModel(this); 0347 m_userUnitFilterModel->setDynamicSortFilter(false); 0348 m_userUnitFilterModel->initFilterMap(filters); 0349 m_userUnitFilterModel->setSourceModel(m_userUnitModel); 0350 ui.tblUserUnits->setModel(m_userUnitFilterModel); 0351 ui.tblUserUnits->sortByColumn(0, Qt::AscendingOrder); 0352 ui.tblUserUnits->resizeColumnsToContents(); 0353 ui.tblUserUnits->setColumnWidth(0, 400); 0354 ui.tblUserUnits->resizeRowsToContents(); 0355 0356 slotChkShowUnits(-1); 0357 } 0358 0359 void MainWindow::setupSessionlist() 0360 { 0361 // Sets up the session list initially 0362 0363 // Register the meta type for storing units 0364 qDBusRegisterMetaType<SystemdSession>(); 0365 0366 // Setup model for session list 0367 m_sessionModel = new QStandardItemModel(this); 0368 0369 // Install eventfilter to capture mouse move events 0370 ui.tblSessions->viewport()->installEventFilter(this); 0371 0372 // Set header row 0373 m_sessionModel->setHorizontalHeaderItem(0, new QStandardItem(i18n("Session ID"))); 0374 m_sessionModel->setHorizontalHeaderItem(1, new QStandardItem(i18n("Session Object Path"))); // This column is hidden 0375 m_sessionModel->setHorizontalHeaderItem(2, new QStandardItem(i18n("State"))); 0376 m_sessionModel->setHorizontalHeaderItem(3, new QStandardItem(i18n("User ID"))); 0377 m_sessionModel->setHorizontalHeaderItem(4, new QStandardItem(i18n("User Name"))); 0378 m_sessionModel->setHorizontalHeaderItem(5, new QStandardItem(i18n("Seat ID"))); 0379 ui.tblSessions->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter); 0380 0381 // Set model for QTableView (should be called after headers are set) 0382 ui.tblSessions->setModel(m_sessionModel); 0383 ui.tblSessions->setColumnHidden(1, true); 0384 0385 // Add all the sessions 0386 slotRefreshSessionList(); 0387 } 0388 0389 void MainWindow::setupTimerlist() 0390 { 0391 // Sets up the timer list initially 0392 0393 // Setup model for timer list 0394 m_timerModel = new QStandardItemModel(this); 0395 0396 // Install eventfilter to capture mouse move events 0397 // ui.tblTimers->viewport()->installEventFilter(this); 0398 0399 // Set header row 0400 m_timerModel->setHorizontalHeaderItem(0, new QStandardItem(i18n("Timer"))); 0401 m_timerModel->setHorizontalHeaderItem(1, new QStandardItem(i18n("Next"))); 0402 m_timerModel->setHorizontalHeaderItem(2, new QStandardItem(i18n("Left"))); 0403 m_timerModel->setHorizontalHeaderItem(3, new QStandardItem(i18n("Last"))); 0404 m_timerModel->setHorizontalHeaderItem(4, new QStandardItem(i18n("Passed"))); 0405 m_timerModel->setHorizontalHeaderItem(5, new QStandardItem(i18n("Activates"))); 0406 ui.tblTimers->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter); 0407 0408 // Set model for QTableView (should be called after headers are set) 0409 ui.tblTimers->setModel(m_timerModel); 0410 ui.tblTimers->sortByColumn(1, Qt::AscendingOrder); 0411 0412 // Setup a timer that updates the left and passed columns every 5secs 0413 timer = new QTimer(this); 0414 connect(timer, SIGNAL(timeout()), this, SLOT(slotUpdateTimers())); 0415 timer->start(1000); 0416 0417 slotRefreshTimerList(); 0418 } 0419 0420 void MainWindow::setupConfFilelist() 0421 { 0422 m_confFileModel = new QStandardItemModel(this); 0423 m_confFileModel->setHorizontalHeaderItem(0, new QStandardItem(i18n("File"))); 0424 m_confFileModel->setHorizontalHeaderItem(1, new QStandardItem(i18n("Modified"))); 0425 m_confFileModel->setHorizontalHeaderItem(2, new QStandardItem(i18n("Description"))); 0426 0427 ui.tblConfFiles->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter); 0428 ui.tblConfFiles->setModel(m_confFileModel); 0429 ui.tblConfFiles->sortByColumn(1, Qt::AscendingOrder); 0430 0431 m_confFileList.append(conffile(QStringLiteral("/etc/systemd/coredump.conf"), 0432 QStringLiteral("coredump.conf"), 0433 i18n("Coredump generation and storage"))); 0434 m_confFileList.append(conffile(QStringLiteral("/etc/systemd/journal-upload.conf"), 0435 QStringLiteral("journal-upload.conf"), 0436 i18n("Send journal messages over network"))); 0437 m_confFileList.append(conffile(QStringLiteral("/etc/systemd/journald.conf"), 0438 QStringLiteral("journald.conf"), 0439 i18n("Journal manager settings"))); 0440 m_confFileList.append(conffile(QStringLiteral("/etc/systemd/logind.conf"), 0441 QStringLiteral("logind.conf"), 0442 i18n("Login manager configuration"))); 0443 m_confFileList.append(conffile(QStringLiteral("/etc/systemd/resolved.conf"), 0444 QStringLiteral("resolved.conf"), 0445 i18n("Network name resolution configuration"))); 0446 m_confFileList.append(conffile(QStringLiteral("/etc/systemd/system.conf"), 0447 QStringLiteral("systemd-system.conf"), 0448 i18n("Systemd daemon configuration"))); 0449 m_confFileList.append(conffile(QStringLiteral("/etc/systemd/timesyncd.conf"), 0450 QStringLiteral("timesyncd.conf"), 0451 i18n("Time synchronization settings"))); 0452 m_confFileList.append(conffile(QStringLiteral("/etc/systemd/user.conf"), 0453 QStringLiteral("systemd-system.conf"), 0454 i18n("Systemd user daemon configuration"))); 0455 0456 slotRefreshConfFileList(); 0457 0458 QFileSystemWatcher *m_fileWatcher = new QFileSystemWatcher; 0459 connect(m_fileWatcher, &QFileSystemWatcher::fileChanged, this, &MainWindow::slotRefreshConfFileList); 0460 foreach (const conffile &f, m_confFileList) { 0461 m_fileWatcher->addPath(f.filePath); 0462 } 0463 } 0464 0465 void MainWindow::slotRefreshConfFileList() 0466 { 0467 qDebug() << "Refreshing config files list..."; 0468 0469 foreach (const conffile &f, m_confFileList) { 0470 if (!QFileInfo::exists(f.filePath)) { 0471 continue; 0472 } 0473 QStandardItem *item = new QStandardItem(QIcon::fromTheme(QStringLiteral("text-plain")), QStringLiteral("%1").arg(f.filePath)); 0474 QStandardItem *item2 = new QStandardItem(QFileInfo(f.filePath).lastModified().toString(QStringLiteral("yyyy-MM-dd HH:mm:ss"))); 0475 QStandardItem *item3 = new QStandardItem(f.description); 0476 m_confFileModel->setItem(m_confFileList.indexOf(f), 0, item); 0477 m_confFileModel->setItem(m_confFileList.indexOf(f), 1, item2); 0478 m_confFileModel->setItem(m_confFileList.indexOf(f), 2, item3); 0479 } 0480 ui.tblConfFiles->resizeColumnsToContents(); 0481 ui.tblConfFiles->resizeRowsToContents(); 0482 } 0483 0484 void MainWindow::slotChkShowUnits(int state) 0485 { 0486 if (state == -1 || 0487 QObject::sender()->objectName() == QLatin1String("chkInactiveUnits") || 0488 QObject::sender()->objectName() == QLatin1String("chkUnloadedUnits")) 0489 { 0490 // System units 0491 if (ui.chkInactiveUnits->isChecked()) 0492 { 0493 ui.chkUnloadedUnits->setEnabled(true); 0494 if (ui.chkUnloadedUnits->isChecked()) 0495 m_systemUnitFilterModel->addFilterRegExp(activeState, QString()); 0496 else 0497 m_systemUnitFilterModel->addFilterRegExp(activeState, QStringLiteral("active")); 0498 } 0499 else 0500 { 0501 ui.chkUnloadedUnits->setEnabled(false); 0502 m_systemUnitFilterModel->addFilterRegExp(activeState, QStringLiteral("^(active)")); 0503 } 0504 m_systemUnitFilterModel->invalidate(); 0505 ui.tblUnits->sortByColumn(ui.tblUnits->horizontalHeader()->sortIndicatorSection(), 0506 ui.tblUnits->horizontalHeader()->sortIndicatorOrder()); 0507 } 0508 if (state == -1 || 0509 QObject::sender()->objectName() == QLatin1String("chkInactiveUserUnits") || 0510 QObject::sender()->objectName() == QLatin1String("chkUnloadedUserUnits")) 0511 { 0512 // User units 0513 if (ui.chkInactiveUserUnits->isChecked()) 0514 { 0515 ui.chkUnloadedUserUnits->setEnabled(true); 0516 if (ui.chkUnloadedUserUnits->isChecked()) 0517 m_userUnitFilterModel->addFilterRegExp(activeState, QString()); 0518 else 0519 m_userUnitFilterModel->addFilterRegExp(activeState, QStringLiteral("active")); 0520 } 0521 else 0522 { 0523 ui.chkUnloadedUserUnits->setEnabled(false); 0524 m_userUnitFilterModel->addFilterRegExp(activeState, QStringLiteral("^(active)")); 0525 } 0526 m_userUnitFilterModel->invalidate(); 0527 ui.tblUserUnits->sortByColumn(ui.tblUserUnits->horizontalHeader()->sortIndicatorSection(), 0528 ui.tblUserUnits->horizontalHeader()->sortIndicatorOrder()); 0529 } 0530 updateUnitCount(); 0531 } 0532 0533 void MainWindow::slotCmbUnitTypes(int index) 0534 { 0535 // Filter unit list for a selected unit type 0536 0537 if (QObject::sender()->objectName() == QLatin1String("cmbUnitTypes")) 0538 { 0539 m_systemUnitFilterModel->addFilterRegExp(unitType, QStringLiteral("(%1)$").arg(unitTypeSufx.at(index))); 0540 m_systemUnitFilterModel->invalidate(); 0541 ui.tblUnits->sortByColumn(ui.tblUnits->horizontalHeader()->sortIndicatorSection(), 0542 ui.tblUnits->horizontalHeader()->sortIndicatorOrder()); 0543 } 0544 else if (QObject::sender()->objectName() == QLatin1String("cmbUserUnitTypes")) 0545 { 0546 m_userUnitFilterModel->addFilterRegExp(unitType, QStringLiteral("(%1)$").arg(unitTypeSufx.at(index))); 0547 m_userUnitFilterModel->invalidate(); 0548 ui.tblUserUnits->sortByColumn(ui.tblUserUnits->horizontalHeader()->sortIndicatorSection(), 0549 ui.tblUserUnits->horizontalHeader()->sortIndicatorOrder()); 0550 } 0551 updateUnitCount(); 0552 } 0553 0554 void MainWindow::slotRefreshUnitsList(bool initial, dbusBus bus) 0555 { 0556 // Updates the unit lists 0557 0558 if (bus == sys) { 0559 0560 qDebug() << "Refreshing system units..."; 0561 0562 // get an updated list of system units via dbus 0563 m_systemUnitsList.clear(); 0564 m_systemUnitsList = getUnitsFromDbus(sys); 0565 m_noActSystemUnits = 0; 0566 foreach (const SystemdUnit &unit, m_systemUnitsList) 0567 { 0568 if (unit.active_state == QLatin1String("active")) 0569 m_noActSystemUnits++; 0570 } 0571 if (!initial) { 0572 m_systemUnitModel->dataChanged(m_systemUnitModel->index(0, 0), m_systemUnitModel->index(m_systemUnitModel->rowCount(), 0)); 0573 m_systemUnitFilterModel->invalidate(); 0574 updateUnitCount(); 0575 slotRefreshTimerList(); 0576 updateActions(); 0577 } 0578 0579 } else if (enableUserUnits && bus == user) { 0580 0581 qDebug() << "Refreshing user units..."; 0582 0583 // get an updated list of user units via dbus 0584 m_userUnitsList.clear(); 0585 m_userUnitsList = getUnitsFromDbus(user); 0586 m_noActUserUnits = 0; 0587 foreach (const SystemdUnit &unit, m_userUnitsList) 0588 { 0589 if (unit.active_state == QLatin1String("active")) 0590 m_noActUserUnits++; 0591 } 0592 if (!initial) { 0593 m_userUnitModel->dataChanged(m_userUnitModel->index(0, 0), m_userUnitModel->index(m_userUnitModel->rowCount(), 0)); 0594 m_userUnitFilterModel->invalidate(); 0595 updateUnitCount(); 0596 slotRefreshTimerList(); 0597 updateActions(); 0598 } 0599 } 0600 } 0601 0602 void MainWindow::slotRefreshSessionList() 0603 { 0604 // Updates the session list 0605 qDebug() << "Refreshing session list..."; 0606 0607 // clear list 0608 m_sessionList.clear(); 0609 0610 // get an updated list of sessions via dbus 0611 QDBusMessage dbusreply = callDbusMethod(QStringLiteral("ListSessions"), logdMgr); 0612 0613 // extract the list of sessions from the reply 0614 const QDBusArgument arg = dbusreply.arguments().at(0).value<QDBusArgument>(); 0615 if (arg.currentType() == QDBusArgument::ArrayType) 0616 { 0617 arg.beginArray(); 0618 while (!arg.atEnd()) 0619 { 0620 SystemdSession session; 0621 arg >> session; 0622 m_sessionList.append(session); 0623 } 0624 arg.endArray(); 0625 } 0626 0627 // Iterate through the new list and compare to model 0628 foreach (const SystemdSession &s, m_sessionList) { 0629 // This is needed to get the "State" property 0630 0631 QList<QStandardItem *> items = m_sessionModel->findItems(s.session_id, Qt::MatchExactly, 0); 0632 0633 if (items.isEmpty()) 0634 { 0635 // New session discovered so add it to the model 0636 QList<QStandardItem *> row; 0637 row << 0638 new QStandardItem(s.session_id) << 0639 new QStandardItem(s.session_path.path()) << 0640 new QStandardItem(getDbusProperty(QStringLiteral("State"), logdSession, s.session_path).toString()) << 0641 new QStandardItem(QString::number(s.user_id)) << 0642 new QStandardItem(s.user_name) << 0643 new QStandardItem(s.seat_id); 0644 m_sessionModel->appendRow(row); 0645 } else { 0646 m_sessionModel->item(items.at(0)->row(), 2)->setData(getDbusProperty(QStringLiteral("State"), logdSession, s.session_path).toString(), Qt::DisplayRole); 0647 } 0648 } 0649 0650 // Check to see if any sessions were removed 0651 if (m_sessionModel->rowCount() != m_sessionList.size()) 0652 { 0653 QList<QPersistentModelIndex> indexes; 0654 // Loop through model and compare to retrieved m_sessionList 0655 for (int row = 0; row < m_sessionModel->rowCount(); ++row) 0656 { 0657 SystemdSession session; 0658 session.session_id = m_sessionModel->index(row,0).data().toString(); 0659 if (!m_sessionList.contains(session)) 0660 { 0661 // Add removed units to list for deletion 0662 // qDebug() << "Unit removed: " << systemUnitModel->index(row,0).data().toString(); 0663 indexes << m_sessionModel->index(row,0); 0664 } 0665 } 0666 // Delete the identified units from model 0667 foreach (const QPersistentModelIndex &i, indexes) 0668 m_sessionModel->removeRow(i.row()); 0669 } 0670 0671 // Update the text color in model 0672 QColor newcolor; 0673 for (int row = 0; row < m_sessionModel->rowCount(); ++row) 0674 { 0675 QBrush newcolor; 0676 const KColorScheme scheme(QPalette::Normal); 0677 if (m_sessionModel->data(m_sessionModel->index(row,2), Qt::DisplayRole) == QLatin1String("active")) 0678 newcolor = scheme.foreground(KColorScheme::PositiveText); 0679 else if (m_sessionModel->data(m_sessionModel->index(row,2), Qt::DisplayRole) == QLatin1String("closing")) 0680 newcolor = scheme.foreground(KColorScheme::InactiveText); 0681 else 0682 newcolor = scheme.foreground(KColorScheme::NormalText); 0683 0684 for (int col = 0; col < m_sessionModel->columnCount(); ++col) 0685 m_sessionModel->setData(m_sessionModel->index(row,col), QVariant(newcolor), Qt::ForegroundRole); 0686 } 0687 ui.tblSessions->resizeColumnsToContents(); 0688 ui.tblSessions->resizeRowsToContents(); 0689 } 0690 0691 void MainWindow::slotRefreshTimerList() 0692 { 0693 // Updates the timer list 0694 qDebug() << "Refreshing timer list..."; 0695 0696 m_timerModel->removeRows(0, m_timerModel->rowCount()); 0697 0698 // Iterate through system unitlist and add timers to the model 0699 foreach (const SystemdUnit &unit, m_systemUnitsList) 0700 { 0701 if (unit.id.endsWith(QLatin1String(".timer")) && 0702 unit.load_state != QLatin1String("unloaded")) { 0703 m_timerModel->appendRow(buildTimerListRow(unit, m_systemUnitsList, sys)); 0704 } 0705 } 0706 0707 // Iterate through user unitlist and add timers to the model 0708 foreach (const SystemdUnit &unit, m_userUnitsList) 0709 { 0710 if (unit.id.endsWith(QLatin1String(".timer")) && 0711 unit.load_state != QLatin1String("unloaded")) { 0712 m_timerModel->appendRow(buildTimerListRow(unit, m_userUnitsList, user)); 0713 } 0714 } 0715 0716 // Update the left and passed columns 0717 slotUpdateTimers(); 0718 0719 ui.tblTimers->resizeColumnsToContents(); 0720 ui.tblTimers->resizeRowsToContents(); 0721 ui.tblTimers->sortByColumn(ui.tblTimers->horizontalHeader()->sortIndicatorSection(), 0722 ui.tblTimers->horizontalHeader()->sortIndicatorOrder()); 0723 } 0724 0725 QList<QStandardItem *> MainWindow::buildTimerListRow(const SystemdUnit &unit, const QVector<SystemdUnit> &list, dbusBus bus) 0726 { 0727 // Builds a row for the timers list 0728 0729 QDBusObjectPath path = unit.unit_path; 0730 QString unitToActivate = getDbusProperty(QStringLiteral("Unit"), sysdTimer, path, bus).toString(); 0731 0732 QDateTime time; 0733 QIcon icon; 0734 if (bus == sys) 0735 icon = QIcon::fromTheme(QStringLiteral("applications-system")); 0736 else 0737 icon = QIcon::fromTheme(QStringLiteral("user-identity")); 0738 0739 // Add the next elapsation point 0740 qlonglong nextElapseMonotonicMsec = getDbusProperty(QStringLiteral("NextElapseUSecMonotonic"), sysdTimer, path, bus).toULongLong() / 1000; 0741 qlonglong nextElapseRealtimeMsec = getDbusProperty(QStringLiteral("NextElapseUSecRealtime"), sysdTimer, path, bus).toULongLong() / 1000; 0742 qlonglong lastTriggerMSec = getDbusProperty(QStringLiteral("LastTriggerUSec"), sysdTimer, path, bus).toULongLong() / 1000; 0743 0744 if (nextElapseMonotonicMsec == 0) 0745 { 0746 // Timer is calendar-based 0747 time.setMSecsSinceEpoch(nextElapseRealtimeMsec); 0748 } 0749 else 0750 { 0751 // Timer is monotonic 0752 time = QDateTime().currentDateTime(); 0753 time = time.addMSecs(nextElapseMonotonicMsec); 0754 0755 // Get the monotonic system clock 0756 struct timespec ts; 0757 if (clock_gettime( CLOCK_MONOTONIC, &ts ) != 0) 0758 qDebug() << "Failed to get the monotonic system clock!"; 0759 0760 // Convert the monotonic system clock to microseconds 0761 qlonglong now_mono_usec = ts.tv_sec * 1000000 + ts.tv_nsec / 1000; 0762 0763 // And subtract it. 0764 time = time.addMSecs(-now_mono_usec/1000); 0765 } 0766 0767 QString next = time.toString(QStringLiteral("yyyy.MM.dd hh:mm:ss")); 0768 0769 QString last; 0770 0771 // use unit object to get last time for activated service 0772 int index = list.indexOf(SystemdUnit(unitToActivate)); 0773 if (index != -1) 0774 { 0775 qlonglong inactivateExitTimestampMsec = 0776 getDbusProperty(QStringLiteral("InactiveExitTimestamp"), sysdUnit, list.at(index).unit_path, bus).toULongLong() / 1000; 0777 0778 if (inactivateExitTimestampMsec == 0) 0779 { 0780 // The unit has not run in this boot 0781 // Use LastTrigger to see if the timer is persistent 0782 if (lastTriggerMSec == 0) 0783 last = QStringLiteral("n/a"); 0784 else 0785 { 0786 time.setMSecsSinceEpoch(lastTriggerMSec); 0787 last = time.toString(QStringLiteral("yyyy.MM.dd hh:mm:ss")); 0788 } 0789 } 0790 else 0791 { 0792 QDateTime time; 0793 time.setMSecsSinceEpoch(inactivateExitTimestampMsec); 0794 last = time.toString(QStringLiteral("yyyy.MM.dd hh:mm:ss")); 0795 } 0796 } 0797 0798 // Set icon for id column 0799 QStandardItem *id = new QStandardItem(unit.id); 0800 id->setData(icon, Qt::DecorationRole); 0801 0802 // Build a row from QStandardItems 0803 QList<QStandardItem *> row; 0804 row << id << 0805 new QStandardItem(next) << 0806 new QStandardItem() << 0807 new QStandardItem(last) << 0808 new QStandardItem() << 0809 new QStandardItem(unitToActivate); 0810 0811 return row; 0812 } 0813 0814 void MainWindow::updateUnitCount() 0815 { 0816 QString systemUnits = i18ncp("First part of 'Total: %1, %2, %3'", 0817 "1 unit", "%1 units", m_systemUnitModel->rowCount()); 0818 QString systemActive = i18ncp("Second part of 'Total: %1, %2, %3'", 0819 "1 active", "%1 active", m_noActSystemUnits); 0820 QString systemDisplayed = i18ncp("Third part of 'Total: %1, %2, %3'", 0821 "1 displayed", "%1 displayed", m_systemUnitFilterModel->rowCount()); 0822 ui.lblUnitCount->setText(i18nc("%1 is '%1 units' and %2 is '%2 active' and %3 is '%3 displayed'", 0823 "Total: %1, %2, %3", systemUnits, systemActive, systemDisplayed)); 0824 0825 QString userUnits = i18ncp("First part of 'Total: %1, %2, %3'", 0826 "1 unit", "%1 units", m_userUnitModel->rowCount()); 0827 QString userActive = i18ncp("Second part of 'Total: %1, %2, %3'", 0828 "1 active", "%1 active", m_noActUserUnits); 0829 QString userDisplayed = i18ncp("Third part of 'Total: %1, %2, %3'", 0830 "1 displayed", "%1 displayed", m_userUnitFilterModel->rowCount()); 0831 ui.lblUserUnitCount->setText(i18nc("%1 is '%1 units' and %2 is '%2 active' and %3 is '%3 displayed'", 0832 "Total: %1, %2, %3", userUnits, userActive, userDisplayed)); 0833 } 0834 0835 void MainWindow::authServiceAction(const QString &service, const QString &path, const QString &iface, const QString &method, const QList<QVariant> &args) 0836 { 0837 // Function to call the helper to authenticate a call to systemd over the system DBus 0838 0839 // Declare a QVariantMap with arguments for the helper 0840 QVariantMap helperArgs; 0841 helperArgs[QStringLiteral("service")] = service; 0842 helperArgs[QStringLiteral("path")] = path; 0843 helperArgs[QStringLiteral("interface")] = iface; 0844 helperArgs[QStringLiteral("method")] = method; 0845 helperArgs[QStringLiteral("argsForCall")] = args; 0846 0847 // Call the helper. This call causes the debug output: "QDBusArgument: read from a write-only object" 0848 KAuth::Action serviceAction(QStringLiteral("org.kde.kcontrol.systemdgenie.dbusaction")); 0849 serviceAction.setHelperId(QStringLiteral("org.kde.kcontrol.systemdgenie")); 0850 serviceAction.setArguments(helperArgs); 0851 0852 KAuth::ExecuteJob *job = serviceAction.execute(); 0853 job->exec(); 0854 0855 if (!job->exec()) 0856 displayMsgWidget(KMessageWidget::Error, 0857 i18n("Unable to authenticate/execute the action: %1", job->error())); 0858 else 0859 { 0860 qDebug() << "DBus action successful."; 0861 // KMessageBox::information(this, i18n("DBus action successful.")); 0862 } 0863 } 0864 0865 void MainWindow::slotConfFileContextMenu(const QPoint &pos) 0866 { 0867 Q_UNUSED(pos) 0868 0869 // Slot for creating the right-click menu in unitlists 0870 if (!factory()) { 0871 return; 0872 } 0873 0874 QMenu *popup = static_cast<QMenu *>(factory()->container(QStringLiteral("context_menu_conf"), this)); 0875 popup->popup(QCursor::pos()); 0876 } 0877 0878 void MainWindow::slotUnitContextMenu(const QPoint &pos) 0879 { 0880 Q_UNUSED(pos) 0881 0882 // Slot for creating the right-click menu in unitlists 0883 if (!factory()) { 0884 return; 0885 } 0886 0887 QMenu *popup = static_cast<QMenu *>(factory()->container(QStringLiteral("context_menu_units"), this)); 0888 popup->popup(QCursor::pos()); 0889 } 0890 0891 void MainWindow::updateActions() 0892 { 0893 //qDebug() << "Updating actions..."; 0894 0895 if ((ui.tabWidget->currentIndex() == 0 && !ui.tblUnits->selectionModel()->selectedRows(0).isEmpty()) || 0896 (ui.tabWidget->currentIndex() == 1 && !ui.tblUserUnits->selectionModel()->selectedRows(0).isEmpty())) 0897 { 0898 QTableView *tblView; 0899 QVector<SystemdUnit> *list; 0900 dbusBus bus; 0901 if (ui.tabWidget->currentIndex() == 0) { 0902 list = &m_systemUnitsList; 0903 tblView = ui.tblUnits; 0904 bus = sys; 0905 } else if (ui.tabWidget->currentIndex() == 1) { 0906 list = &m_userUnitsList; 0907 tblView = ui.tblUserUnits; 0908 bus = user; 0909 } else { 0910 m_startUnitAction->setEnabled(false); 0911 m_stopUnitAction->setEnabled(false); 0912 m_restartUnitAction->setEnabled(false); 0913 m_reloadUnitAction->setEnabled(false); 0914 m_enableUnitAction->setEnabled(false); 0915 m_disableUnitAction->setEnabled(false); 0916 m_maskUnitAction->setEnabled(false); 0917 m_unmaskUnitAction->setEnabled(false); 0918 m_editUnitFileAction->setEnabled(false); 0919 return; 0920 } 0921 0922 0923 // Find name and object path of unit 0924 const QString unit = tblView->selectionModel()->selectedRows(0).at(0).data().toString(); 0925 Q_ASSERT(!unit.isEmpty()); 0926 Q_ASSERT(list->contains(SystemdUnit(unit))); 0927 const QDBusObjectPath pathUnit = list->at(list->indexOf(SystemdUnit(unit))).unit_path; 0928 0929 // Check capabilities of unit 0930 bool CanStart = false; 0931 bool CanStop = false; 0932 bool CanReload = false; 0933 bool hasUnitObject = true; 0934 //bool CanIsolate = false; 0935 QString LoadState; 0936 QString ActiveState; 0937 QString unitFileState; 0938 if (!pathUnit.path().isEmpty() && 0939 getDbusProperty(QStringLiteral("Test"), sysdUnit, pathUnit, bus).toString() != QLatin1String("invalidIface")) 0940 { 0941 // Unit has a Unit DBus object, fetch properties 0942 LoadState = getDbusProperty(QStringLiteral("LoadState"), sysdUnit, pathUnit, bus).toString(); 0943 ActiveState = getDbusProperty(QStringLiteral("ActiveState"), sysdUnit, pathUnit, bus).toString(); 0944 CanStart = getDbusProperty(QStringLiteral("CanStart"), sysdUnit, pathUnit, bus).toBool(); 0945 CanStop = getDbusProperty(QStringLiteral("CanStop"), sysdUnit, pathUnit, bus).toBool(); 0946 CanReload = getDbusProperty(QStringLiteral("CanReload"), sysdUnit, pathUnit, bus).toBool(); 0947 //CanIsolate = getDbusProperty(QStringLiteral("CanIsolate"), sysdUnit, pathUnit, bus).toBool(); 0948 0949 unitFileState = getDbusProperty(QStringLiteral("UnitFileState"), sysdUnit, pathUnit, bus).toString(); 0950 } else { 0951 hasUnitObject = false; 0952 // Get UnitFileState from Manager object. 0953 unitFileState = callDbusMethod(QStringLiteral("GetUnitFileState"), sysdMgr, bus, QVariantList{unit}).arguments().at(0).toString(); 0954 } 0955 0956 // Check if unit has a unit file, if not disable editing. 0957 QString frpath; 0958 int index = list->indexOf(SystemdUnit(unit)); 0959 if (index != -1) { 0960 frpath = list->at(index).unit_file; 0961 } 0962 0963 bool isUnitSelected = !unit.isEmpty(); 0964 0965 m_startUnitAction->setEnabled(isUnitSelected && 0966 (CanStart || !hasUnitObject) && 0967 ActiveState != QLatin1String("active")); 0968 0969 m_stopUnitAction->setEnabled(isUnitSelected && 0970 CanStop && 0971 ActiveState != QLatin1String("inactive") && 0972 ActiveState != QLatin1String("failed")); 0973 0974 m_restartUnitAction->setEnabled(isUnitSelected && 0975 CanStart && 0976 ActiveState != QLatin1String("inactive") && 0977 ActiveState != QLatin1String("failed") && 0978 !LoadState.isEmpty()); 0979 0980 m_reloadUnitAction->setEnabled(isUnitSelected && 0981 CanReload && 0982 ActiveState != QLatin1String("inactive") && 0983 ActiveState != QLatin1String("failed")); 0984 0985 m_enableUnitAction->setEnabled(isUnitSelected && 0986 unitFileState == QLatin1String("disabled")); 0987 0988 m_disableUnitAction->setEnabled(isUnitSelected && 0989 unitFileState == QLatin1String("enabled")); 0990 0991 m_maskUnitAction->setEnabled(isUnitSelected && 0992 LoadState != QLatin1String("masked")); 0993 0994 m_unmaskUnitAction->setEnabled(isUnitSelected && 0995 LoadState == QLatin1String("masked")); 0996 0997 m_editUnitFileAction->setEnabled(isUnitSelected && 0998 !frpath.isEmpty()); 0999 } else { 1000 m_startUnitAction->setEnabled(false); 1001 m_stopUnitAction->setEnabled(false); 1002 m_restartUnitAction->setEnabled(false); 1003 m_reloadUnitAction->setEnabled(false); 1004 m_enableUnitAction->setEnabled(false); 1005 m_disableUnitAction->setEnabled(false); 1006 m_maskUnitAction->setEnabled(false); 1007 m_unmaskUnitAction->setEnabled(false); 1008 m_editUnitFileAction->setEnabled(false); 1009 } 1010 1011 m_editConfFileAction->setEnabled(ui.tabWidget->currentIndex() == 2 && 1012 !ui.tblConfFiles->selectionModel()->selectedRows(0).isEmpty()); 1013 m_openManPageAction->setEnabled(ui.tabWidget->currentIndex() == 2 && 1014 !ui.tblConfFiles->selectionModel()->selectedRows(0).isEmpty()); 1015 1016 m_activateSessionAction->setEnabled(ui.tabWidget->currentIndex() == 3 && 1017 !ui.tblSessions->selectionModel()->selectedRows(0).isEmpty() && 1018 ui.tblSessions->selectionModel()->selectedRows(2).at(0).data().toString() != QLatin1String("active")); 1019 1020 m_terminateSessionAction->setEnabled(ui.tabWidget->currentIndex() == 3 && 1021 !ui.tblSessions->selectionModel()->selectedRows(0).isEmpty()); 1022 1023 QDBusObjectPath pathSession; 1024 if (!ui.tblSessions->selectionModel()->selectedRows(0).isEmpty()) { 1025 pathSession = QDBusObjectPath(ui.tblSessions->selectionModel()->selectedRows(1).at(0).data().toString()); 1026 } 1027 m_lockSessionAction->setEnabled(ui.tabWidget->currentIndex() == 3 && 1028 !ui.tblSessions->selectionModel()->selectedRows(0).isEmpty() && 1029 getDbusProperty(QStringLiteral("Type"), logdSession, pathSession).toString() != QLatin1String("tty")); 1030 } 1031 1032 void MainWindow::setupActions() 1033 { 1034 KStandardAction::quit(qApp, SLOT(quit()), actionCollection()); 1035 1036 m_refreshAction = new QAction(this); 1037 m_refreshAction->setText(i18n("&Refresh")); 1038 m_refreshAction->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); 1039 m_refreshAction->setToolTip(i18nc("@info:tooltip", "Refresh")); 1040 actionCollection()->setDefaultShortcut(m_refreshAction, Qt::Key_F5); 1041 actionCollection()->addAction(QStringLiteral("refresh"), m_refreshAction); 1042 connect(m_refreshAction, &QAction::triggered, this, &MainWindow::slotRefreshAll); 1043 1044 m_startUnitAction = new QAction(this); 1045 m_startUnitAction->setText(i18n("Start Unit")); 1046 m_startUnitAction->setIcon(QIcon::fromTheme(QStringLiteral("kt-start"))); 1047 actionCollection()->addAction(QStringLiteral("start-unit"), m_startUnitAction); 1048 connect(m_startUnitAction, &QAction::triggered, this, [this]() { executeUnitAction(QStringLiteral("StartUnit")); }); 1049 1050 m_stopUnitAction = new QAction(this); 1051 m_stopUnitAction->setText(i18n("Stop Unit")); 1052 m_stopUnitAction->setIcon(QIcon::fromTheme(QStringLiteral("kt-stop"))); 1053 actionCollection()->addAction(QStringLiteral("stop-unit"), m_stopUnitAction); 1054 connect(m_stopUnitAction, &QAction::triggered, this, [this]() { executeUnitAction(QStringLiteral("StopUnit")); }); 1055 1056 m_reloadUnitAction = new QAction(this); 1057 m_reloadUnitAction->setText(i18n("Reload Unit")); 1058 m_reloadUnitAction->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); 1059 actionCollection()->addAction(QStringLiteral("reload-unit"), m_reloadUnitAction); 1060 connect(m_reloadUnitAction, &QAction::triggered, this, [this]() { executeUnitAction(QStringLiteral("ReloadUnit")); }); 1061 1062 m_restartUnitAction = new QAction(this); 1063 m_restartUnitAction->setText(i18n("Restart Unit")); 1064 m_restartUnitAction->setIcon(QIcon::fromTheme(QStringLiteral("start-over"))); 1065 actionCollection()->addAction(QStringLiteral("restart-unit"), m_restartUnitAction); 1066 connect(m_restartUnitAction, &QAction::triggered, this, [this]() { executeUnitAction(QStringLiteral("RestartUnit")); }); 1067 1068 m_enableUnitAction = new QAction(this); 1069 m_enableUnitAction->setText(i18n("Enable Unit")); 1070 m_enableUnitAction->setIcon(QIcon::fromTheme(QStringLiteral("archive-insert"))); 1071 actionCollection()->addAction(QStringLiteral("enable-unit"), m_enableUnitAction); 1072 connect(m_enableUnitAction, &QAction::triggered, this, [this]() { executeUnitAction(QStringLiteral("EnableUnitFiles")); }); 1073 1074 m_disableUnitAction = new QAction(this); 1075 m_disableUnitAction->setText(i18n("Disable Unit")); 1076 m_disableUnitAction->setIcon(QIcon::fromTheme(QStringLiteral("document-close"))); 1077 actionCollection()->addAction(QStringLiteral("disable-unit"), m_disableUnitAction); 1078 connect(m_disableUnitAction, &QAction::triggered, this, [this]() { executeUnitAction(QStringLiteral("DisableUnitFiles")); }); 1079 1080 m_maskUnitAction = new QAction(this); 1081 m_maskUnitAction->setText(i18n("Mask Unit")); 1082 m_maskUnitAction->setIcon(QIcon::fromTheme(QStringLiteral("password-show-off"))); 1083 actionCollection()->addAction(QStringLiteral("mask-unit"), m_maskUnitAction); 1084 connect(m_maskUnitAction, &QAction::triggered, this, [this]() { executeUnitAction(QStringLiteral("MaskUnitFiles")); }); 1085 1086 m_unmaskUnitAction = new QAction(this); 1087 m_unmaskUnitAction->setText(i18n("Unmask Unit")); 1088 m_unmaskUnitAction->setIcon(QIcon::fromTheme(QStringLiteral("password-show-on"))); 1089 actionCollection()->addAction(QStringLiteral("unmask-unit"), m_unmaskUnitAction); 1090 connect(m_unmaskUnitAction, &QAction::triggered, this, [this]() { executeUnitAction(QStringLiteral("UnmaskUnitFiles")); }); 1091 1092 m_reloadSystemDaemonAction = new QAction(this); 1093 m_reloadSystemDaemonAction->setText(i18n("Re&load systemd")); 1094 m_reloadSystemDaemonAction->setToolTip(i18nc("@info:tooltip", "Click to reload all unit files")); 1095 m_reloadSystemDaemonAction->setIcon(QIcon::fromTheme(QStringLiteral("configure-shortcuts"))); 1096 actionCollection()->addAction(QStringLiteral("reload-daemon-system"), m_reloadSystemDaemonAction); 1097 connect(m_reloadSystemDaemonAction, &QAction::triggered, this, [this]() { executeSystemDaemonAction(QStringLiteral("Reload")); }); 1098 1099 m_reexecSystemDaemonAction = new QAction(this); 1100 m_reexecSystemDaemonAction->setText(i18n("Re-e&xecute systemd")); 1101 m_reexecSystemDaemonAction->setToolTip(i18nc("@info:tooltip", "Click to re-execute the systemd daemon")); 1102 m_reexecSystemDaemonAction->setIcon(QIcon::fromTheme(QStringLiteral("configure-shortcuts"))); 1103 actionCollection()->addAction(QStringLiteral("reexec-daemon-system"), m_reexecSystemDaemonAction); 1104 connect(m_reexecSystemDaemonAction, &QAction::triggered, this, [this]() { executeSystemDaemonAction(QStringLiteral("Reexecute")); }); 1105 1106 m_reloadUserDaemonAction = new QAction(this); 1107 m_reloadUserDaemonAction->setText(i18n("Re&load user systemd")); 1108 m_reloadUserDaemonAction->setToolTip(i18nc("@info:tooltip", "Click to reload all user unit files")); 1109 m_reloadUserDaemonAction->setIcon(QIcon::fromTheme(QStringLiteral("user"))); 1110 actionCollection()->addAction(QStringLiteral("reload-daemon-user"), m_reloadUserDaemonAction); 1111 connect(m_reloadUserDaemonAction, &QAction::triggered, this, [this]() { executeUserDaemonAction(QStringLiteral("Reload")); }); 1112 1113 m_reexecUserDaemonAction = new QAction(this); 1114 m_reexecUserDaemonAction->setText(i18n("Re-e&xecute user systemd")); 1115 m_reexecUserDaemonAction->setToolTip(i18nc("@info:tooltip", "Click to re-execute the user systemd daemon")); 1116 m_reexecUserDaemonAction->setIcon(QIcon::fromTheme(QStringLiteral("user"))); 1117 actionCollection()->addAction(QStringLiteral("reexec-daemon-user"), m_reexecUserDaemonAction); 1118 connect(m_reexecUserDaemonAction, &QAction::triggered, this, [this]() { executeUserDaemonAction(QStringLiteral("Reexecute")); }); 1119 1120 m_editUnitFileAction = new QAction(this); 1121 m_editUnitFileAction->setText(i18n("Edit Unit File")); 1122 m_editUnitFileAction->setIcon(QIcon::fromTheme(QStringLiteral("editor"))); 1123 actionCollection()->addAction(QStringLiteral("edit-unitfile"), m_editUnitFileAction); 1124 connect(m_editUnitFileAction, &QAction::triggered, this, &MainWindow::slotEditUnitFile); 1125 1126 m_editConfFileAction = new QAction(this); 1127 m_editConfFileAction->setText(i18n("Edit Configuration File")); 1128 m_editConfFileAction->setIcon(QIcon::fromTheme(QStringLiteral("editor"))); 1129 actionCollection()->addAction(QStringLiteral("edit-conffile"), m_editConfFileAction); 1130 connect(m_editConfFileAction, &QAction::triggered, this, &MainWindow::slotEditConfFile); 1131 1132 m_openManPageAction = new QAction(this); 1133 m_openManPageAction->setText(i18n("Open Man Page")); 1134 m_openManPageAction->setIcon(QIcon::fromTheme(QStringLiteral("help-contents"))); 1135 actionCollection()->addAction(QStringLiteral("open-manpage"), m_openManPageAction); 1136 connect(m_openManPageAction, &QAction::triggered, this, &MainWindow::slotOpenManPage); 1137 1138 m_activateSessionAction = new QAction(this); 1139 m_activateSessionAction->setText(i18n("Activate Session")); 1140 m_activateSessionAction->setIcon(QIcon::fromTheme(QStringLiteral("kt-start"))); 1141 actionCollection()->addAction(QStringLiteral("activate-session"), m_activateSessionAction); 1142 connect(m_activateSessionAction, &QAction::triggered, this, [this]() { executeSessionAction(QStringLiteral("Activate")); }); 1143 1144 m_terminateSessionAction = new QAction(this); 1145 m_terminateSessionAction->setText(i18n("Terminate Session")); 1146 m_terminateSessionAction->setIcon(QIcon::fromTheme(QStringLiteral("kt-remove"))); 1147 actionCollection()->addAction(QStringLiteral("terminate-session"), m_terminateSessionAction); 1148 connect(m_terminateSessionAction, &QAction::triggered, this, [this]() { executeSessionAction(QStringLiteral("Terminate")); }); 1149 1150 m_lockSessionAction = new QAction(this); 1151 m_lockSessionAction->setText(i18n("Lock Session")); 1152 m_lockSessionAction->setIcon(QIcon::fromTheme(QStringLiteral("lock"))); 1153 actionCollection()->addAction(QStringLiteral("lock-session"), m_lockSessionAction); 1154 connect(m_lockSessionAction, &QAction::triggered, this, [this]() { executeSessionAction(QStringLiteral("Lock")); }); 1155 1156 updateActions(); 1157 } 1158 1159 void MainWindow::slotOpenManPage() 1160 { 1161 if (ui.tblConfFiles->selectionModel()->selectedRows(0).isEmpty()) { 1162 return; 1163 } 1164 1165 QModelIndex index = ui.tblConfFiles->selectionModel()->selectedRows(0).at(0); 1166 Q_ASSERT(index.isValid()); 1167 1168 const QString manPage = m_confFileList.at(m_confFileList.indexOf(conffile(index.data().toString()))).manPage; 1169 1170 const QString KHelpCenterExec = QStandardPaths::findExecutable(QStringLiteral("khelpcenter")); 1171 if (KHelpCenterExec.isEmpty()) { 1172 KMessageBox::error(this, i18n("KHelpCenter executable not found in path. Please install KHelpCenter to view man pages.")); 1173 return; 1174 } 1175 1176 QProcess *helpcenter = new QProcess(this); 1177 helpcenter->start(KHelpCenterExec, QStringList{QStringLiteral("man:/%1").arg(manPage)}); 1178 } 1179 1180 void MainWindow::executeUnitAction(const QString &method) 1181 { 1182 QTableView *tblView; 1183 if (ui.tabWidget->currentIndex() == 0) { 1184 tblView = ui.tblUnits; 1185 } else { 1186 tblView = ui.tblUserUnits; 1187 } 1188 1189 if (tblView->selectionModel()->selectedRows(0).isEmpty()) { 1190 return; 1191 } 1192 1193 QModelIndex index = tblView->selectionModel()->selectedRows(0).at(0); 1194 Q_ASSERT(index.isValid()); 1195 1196 QString unit = index.data().toString(); 1197 //qDebug() << "unit:" << unit; 1198 1199 QVariantList args; 1200 if (method == QLatin1String("EnableUnitFiles") || 1201 method == QLatin1String("MaskUnitFiles")) { 1202 args << QVariant(QStringList{unit}) << false << true; 1203 } else if (method == QLatin1String("DisableUnitFiles") || 1204 method == QLatin1String("UnmaskUnitFiles")) { 1205 args << QVariant(QStringList{unit}) << false; 1206 } else { 1207 args = QVariantList{unit, QStringLiteral("replace")}; 1208 } 1209 1210 if (ui.tabWidget->currentIndex() == 0) { 1211 authServiceAction(connSystemd, pathSysdMgr, ifaceMgr, method, args); 1212 } else if (ui.tabWidget->currentIndex() == 1) { 1213 // user unit 1214 callDbusMethod(method, sysdMgr, user, args); 1215 } 1216 } 1217 1218 void MainWindow::executeSessionAction(const QString &method) 1219 { 1220 if (ui.tblSessions->selectionModel()->selectedRows(0).isEmpty()) { 1221 return; 1222 } 1223 1224 QModelIndex index = ui.tblSessions->selectionModel()->selectedRows(0).at(0); 1225 Q_ASSERT(index.isValid()); 1226 1227 QDBusObjectPath pathSession = QDBusObjectPath(ui.tblSessions->selectionModel()->selectedRows(1).at(0).data().toString()); 1228 1229 authServiceAction(connLogind, pathSession.path(), ifaceSession, method, QVariantList()); 1230 } 1231 1232 void MainWindow::executeSystemDaemonAction(const QString &method) 1233 { 1234 // Execute the DBus actions 1235 if (!method.isEmpty()) { 1236 authServiceAction(connSystemd, pathSysdMgr, ifaceMgr, method, QVariantList()); 1237 } 1238 } 1239 1240 void MainWindow::executeUserDaemonAction(const QString &method) 1241 { 1242 if (!method.isEmpty()) 1243 { 1244 callDbusMethod(method, sysdMgr, user, QVariantList()); 1245 if (QRegularExpression(QStringLiteral("EnableUnitFiles|DisableUnitFiles|MaskUnitFiles|UnmaskUnitFiles")).match(method).hasMatch()) { 1246 callDbusMethod(QStringLiteral("Reload"), sysdMgr, user); 1247 } 1248 } 1249 } 1250 1251 void MainWindow::slotRefreshAll() 1252 { 1253 slotRefreshUnitsList(false, sys); 1254 slotRefreshUnitsList(false, user); 1255 slotRefreshConfFileList(); 1256 slotRefreshSessionList(); 1257 } 1258 1259 void MainWindow::slotSessionContextMenu(const QPoint &pos) 1260 { 1261 // Slot for creating the right-click menu in the session list 1262 1263 Q_UNUSED(pos) 1264 1265 if (!factory()) { 1266 return; 1267 } 1268 1269 QMenu *popup = static_cast<QMenu *>(factory()->container(QStringLiteral("context_menu_session"), this)); 1270 popup->popup(QCursor::pos()); 1271 } 1272 1273 1274 bool MainWindow::eventFilter(QObject *obj, QEvent* event) 1275 { 1276 // Eventfilter for catching mouse move events over session list 1277 // Used for dynamically generating tooltips 1278 1279 if (event->type() == QEvent::MouseMove && obj->parent()->objectName() == QLatin1String("tblSessions")) 1280 { 1281 // Session list 1282 QMouseEvent *me = static_cast<QMouseEvent*>(event); 1283 QModelIndex inSessionModel = ui.tblSessions->indexAt(me->pos()); 1284 if (!inSessionModel.isValid()) 1285 return false; 1286 1287 if (m_sessionModel->itemFromIndex(inSessionModel)->row() != lastSessionRowChecked) 1288 { 1289 // Cursor moved to a different row. Only build tooltips when moving 1290 // cursor to a new row to avoid excessive DBus calls. 1291 1292 QString selSession = ui.tblSessions->model()->index(ui.tblSessions->indexAt(me->pos()).row(),0).data().toString(); 1293 QDBusObjectPath spath = QDBusObjectPath(ui.tblSessions->model()->index(ui.tblSessions->indexAt(me->pos()).row(),1).data().toString()); 1294 1295 QString toolTipText; 1296 toolTipText.append(QStringLiteral("<FONT COLOR=white>")); 1297 toolTipText.append(QStringLiteral("<b>%1</b><hr>").arg(selSession)); 1298 1299 // Use the DBus interface to get session properties 1300 if (getDbusProperty(QStringLiteral("test"), logdSession, spath) != QLatin1String("invalidIface")) 1301 { 1302 // Session has a valid session DBus object 1303 toolTipText.append(i18n("<b>VT:</b> %1", getDbusProperty(QStringLiteral("VTNr"), logdSession, spath).toString())); 1304 1305 QString remoteHost = getDbusProperty(QStringLiteral("RemoteHost"), logdSession, spath).toString(); 1306 if (getDbusProperty(QStringLiteral("Remote"), logdSession, spath).toBool()) 1307 { 1308 toolTipText.append(i18n("<br><b>Remote host:</b> %1", remoteHost)); 1309 toolTipText.append(i18n("<br><b>Remote user:</b> %1", getDbusProperty(QStringLiteral("RemoteUser"), logdSession, spath).toString())); 1310 } 1311 toolTipText.append(i18n("<br><b>Service:</b> %1", getDbusProperty(QStringLiteral("Service"), logdSession, spath).toString())); 1312 toolTipText.append(i18n("<br><b>Leader (PID):</b> %1", getDbusProperty(QStringLiteral("Leader"), logdSession, spath).toString())); 1313 1314 QString type = getDbusProperty(QStringLiteral("Type"), logdSession, spath).toString(); 1315 toolTipText.append(i18n("<br><b>Type:</b> %1", type)); 1316 if (type == QLatin1String("x11")) 1317 toolTipText.append(i18n(" (display %1)", getDbusProperty(QStringLiteral("Display"), logdSession, spath).toString())); 1318 else if (type == QLatin1String("tty")) 1319 { 1320 QString path, tty = getDbusProperty(QStringLiteral("TTY"), logdSession, spath).toString(); 1321 if (!tty.isEmpty()) 1322 path = tty; 1323 else if (!remoteHost.isEmpty()) 1324 path = QStringLiteral("%1@%2").arg(getDbusProperty(QStringLiteral("Name"), logdSession, spath).toString()).arg(remoteHost); 1325 toolTipText.append(QStringLiteral(" (%1)").arg(path)); 1326 } 1327 toolTipText.append(i18n("<br><b>Class:</b> %1", getDbusProperty(QStringLiteral("Class"), logdSession, spath).toString())); 1328 toolTipText.append(i18n("<br><b>State:</b> %1", getDbusProperty(QStringLiteral("State"), logdSession, spath).toString())); 1329 toolTipText.append(i18n("<br><b>Scope:</b> %1", getDbusProperty(QStringLiteral("Scope"), logdSession, spath).toString())); 1330 1331 1332 toolTipText.append(i18n("<br><b>Created: </b>")); 1333 if (getDbusProperty(QStringLiteral("Timestamp"), logdSession, spath).toULongLong() == 0) 1334 toolTipText.append(QStringLiteral("n/a")); 1335 else 1336 { 1337 QDateTime time; 1338 time.setMSecsSinceEpoch(getDbusProperty(QStringLiteral("Timestamp"), logdSession, spath).toULongLong()/1000); 1339 toolTipText.append(time.toString()); 1340 } 1341 } 1342 1343 toolTipText.append(QStringLiteral("</FONT")); 1344 m_sessionModel->itemFromIndex(inSessionModel)->setToolTip(toolTipText); 1345 1346 lastSessionRowChecked = m_sessionModel->itemFromIndex(inSessionModel)->row(); 1347 return true; 1348 1349 } // Row was different 1350 } 1351 return false; 1352 // return true; 1353 } 1354 1355 void MainWindow::slotSystemSystemdReloading(bool status) 1356 { 1357 if (status) { 1358 qDebug() << "System daemon reloading..."; 1359 } else { 1360 qDebug() << "System daemon reloaded"; 1361 m_lblLog->setText(i18n("%1: Systemd daemon reloaded...", QDateTime::currentDateTime().toString(QStringLiteral("hh:mm:ss")))); 1362 slotRefreshUnitsList(false, sys); 1363 } 1364 } 1365 1366 void MainWindow::slotUserSystemdReloading(bool status) 1367 { 1368 if (status) { 1369 qDebug() << "User daemon reloading..."; 1370 } else { 1371 qDebug() << "User daemon reloaded"; 1372 m_lblLog->setText(i18n("%1: User daemon reloaded...", QDateTime::currentDateTime().toString(QStringLiteral("hh:mm:ss")))); 1373 slotRefreshUnitsList(false, user); 1374 } 1375 } 1376 1377 /* 1378 void MainWindow::slotSystemUnitNew(const QString &id, const QDBusObjectPath &path) 1379 { 1380 //qDebug() << "SystemUnitNew: " << id << path.path(); 1381 } 1382 1383 void MainWindow::slotSystemUnitRemoved(const QString &id, const QDBusObjectPath &path) 1384 { 1385 //qDebug() << "SystemUnitRemoved: " << id << path.path(); 1386 } 1387 */ 1388 1389 void MainWindow::slotSystemJobNew(uint id, const QDBusObjectPath &path, const QString &unit) 1390 { 1391 qDebug() << "SystemJobNew: " << id << path.path() << unit; 1392 } 1393 1394 void MainWindow::slotSystemJobRemoved(uint id, const QDBusObjectPath &path, const QString &unit, const QString &result) 1395 { 1396 qDebug() << "SystmJobRemoved: " << id << path.path() << unit << result; 1397 slotRefreshUnitsList(false, sys); 1398 } 1399 1400 void MainWindow::slotUserJobNew(uint id, const QDBusObjectPath &path, const QString &unit) 1401 { 1402 qDebug() << "UserJobNew: " << id << path.path() << unit; 1403 } 1404 1405 void MainWindow::slotUserJobRemoved(uint id, const QDBusObjectPath &path, const QString &unit, const QString &result) 1406 { 1407 qDebug() << "UserJobRemoved: " << id << path.path() << unit << result; 1408 slotRefreshUnitsList(false, user); 1409 } 1410 1411 void MainWindow::slotSystemUnitFilesChanged() 1412 { 1413 //qDebug() << "System unitFilesChanged"; 1414 slotRefreshUnitsList(false, sys); 1415 m_lblLog->setText(i18n("%1: System units changed...", QDateTime::currentDateTime().toString(QStringLiteral("hh:mm:ss")))); 1416 } 1417 1418 void MainWindow::slotUserUnitFilesChanged() 1419 { 1420 //qDebug() << "User unitFilesChanged"; 1421 slotRefreshUnitsList(false, user); 1422 } 1423 1424 void MainWindow::slotSystemPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps) 1425 { 1426 Q_UNUSED(changedProps) 1427 Q_UNUSED(invalidatedProps) 1428 1429 qDebug() << "systemPropertiesChanged:" << iface; // << changedProps << invalidatedProps; 1430 slotRefreshUnitsList(false, sys); 1431 } 1432 1433 void MainWindow::slotUserPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps) 1434 { 1435 Q_UNUSED(changedProps) 1436 Q_UNUSED(invalidatedProps) 1437 1438 qDebug() << "userPropertiesChanged:" << iface; // << changedProps << invalidatedProps; 1439 slotRefreshUnitsList(false, user); 1440 } 1441 1442 void MainWindow::slotLogindPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps) 1443 { 1444 Q_UNUSED(iface) 1445 Q_UNUSED(changedProps) 1446 Q_UNUSED(invalidatedProps) 1447 1448 // qDebug() << "Logind properties changed on iface " << iface_name; 1449 slotRefreshSessionList(); 1450 } 1451 1452 void MainWindow::slotLeSearchUnitChanged(QString term) 1453 { 1454 if (QObject::sender()->objectName() == QLatin1String("leSearchUnit")) 1455 { 1456 m_systemUnitFilterModel->addFilterRegExp(unitName, term); 1457 m_systemUnitFilterModel->invalidate(); 1458 ui.tblUnits->sortByColumn(ui.tblUnits->horizontalHeader()->sortIndicatorSection(), 1459 ui.tblUnits->horizontalHeader()->sortIndicatorOrder()); 1460 } 1461 else if (QObject::sender()->objectName() == QLatin1String("leSearchUserUnit")) 1462 { 1463 m_userUnitFilterModel->addFilterRegExp(unitName, term); 1464 m_userUnitFilterModel->invalidate(); 1465 ui.tblUserUnits->sortByColumn(ui.tblUserUnits->horizontalHeader()->sortIndicatorSection(), 1466 ui.tblUserUnits->horizontalHeader()->sortIndicatorOrder()); 1467 } 1468 updateUnitCount(); 1469 } 1470 1471 void MainWindow::slotUpdateTimers() 1472 { 1473 // Updates the left and passed columns in the timers list 1474 for (int row = 0; row < m_timerModel->rowCount(); ++row) 1475 { 1476 QDateTime next = m_timerModel->index(row, 1).data().toDateTime(); 1477 QDateTime last = m_timerModel->index(row, 3).data().toDateTime(); 1478 QDateTime current = QDateTime().currentDateTime(); 1479 qlonglong leftSecs = current.secsTo(next); 1480 qlonglong passedSecs = last.secsTo(current); 1481 1482 QString left; 1483 if (leftSecs >= 31536000) 1484 left = QStringLiteral("%1 years").arg(leftSecs / 31536000); 1485 else if (leftSecs >= 604800) 1486 left = QStringLiteral("%1 weeks").arg(leftSecs / 604800); 1487 else if (leftSecs >= 86400) 1488 left = QStringLiteral("%1 days").arg(leftSecs / 86400); 1489 else if (leftSecs >= 3600) 1490 left = QStringLiteral("%1 hr").arg(leftSecs / 3600); 1491 else if (leftSecs >= 60) 1492 left = QStringLiteral("%1 min").arg(leftSecs / 60); 1493 else if (leftSecs < 0) 1494 left = QStringLiteral("0 s"); 1495 else 1496 left = QStringLiteral("%1 s").arg(leftSecs); 1497 m_timerModel->setData(m_timerModel->index(row, 2), left); 1498 1499 QString passed; 1500 if (m_timerModel->index(row, 3).data() == QStringLiteral("n/a")) 1501 passed = QStringLiteral("n/a"); 1502 else if (passedSecs >= 31536000) 1503 passed = QStringLiteral("%1 years").arg(passedSecs / 31536000); 1504 else if (passedSecs >= 604800) 1505 passed = QStringLiteral("%1 weeks").arg(passedSecs / 604800); 1506 else if (passedSecs >= 86400) 1507 passed = QStringLiteral("%1 days").arg(passedSecs / 86400); 1508 else if (passedSecs >= 3600) 1509 passed = QStringLiteral("%1 hr").arg(passedSecs / 3600); 1510 else if (passedSecs >= 60) 1511 passed = QStringLiteral("%1 min").arg(passedSecs / 60); 1512 else if (passedSecs < 0) 1513 passed = QStringLiteral("0 s"); 1514 else 1515 passed = QStringLiteral("%1 s").arg(passedSecs); 1516 m_timerModel->setData(m_timerModel->index(row, 4), passed); 1517 } 1518 } 1519 1520 void MainWindow::slotEditUnitFile() 1521 { 1522 QTableView *tblView; 1523 QVector<SystemdUnit> *list; 1524 if (ui.tabWidget->currentIndex() == 0) { 1525 tblView = ui.tblUnits; 1526 list = &m_systemUnitsList; 1527 } else { 1528 tblView = ui.tblUserUnits; 1529 list = &m_userUnitsList; 1530 } 1531 1532 if (tblView->selectionModel()->selectedRows(0).isEmpty()) { 1533 return; 1534 } 1535 1536 QModelIndex index = tblView->selectionModel()->selectedRows(0).at(0); 1537 Q_ASSERT(index.isValid()); 1538 QString file = list->at(list->indexOf(SystemdUnit(index.data().toString()))).unit_file; 1539 1540 openEditor(file); 1541 } 1542 1543 void MainWindow::slotEditConfFile() 1544 { 1545 if (ui.tblConfFiles->selectionModel()->selectedRows(0).isEmpty()) { 1546 return; 1547 } 1548 1549 QModelIndex index = ui.tblConfFiles->selectionModel()->selectedRows(0).at(0); 1550 Q_ASSERT(index.isValid()); 1551 1552 QString file = index.data().toString(); 1553 1554 openEditor(file); 1555 } 1556 1557 void MainWindow::openEditor(const QString &file) 1558 { 1559 // Using a QPointer is safer for modal dialogs. 1560 // See: https://blogs.kde.org/node/3919 1561 QPointer<QDialog> dlgEditor = new QDialog(this); 1562 dlgEditor->setWindowTitle(i18n("Editing %1", file.section(QLatin1Char('/'), -1))); 1563 1564 QPlainTextEdit *textEdit = new QPlainTextEdit(dlgEditor); 1565 textEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); 1566 1567 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | 1568 QDialogButtonBox::Cancel, 1569 dlgEditor); 1570 connect(buttonBox, SIGNAL(accepted()), dlgEditor, SLOT(accept())); 1571 connect(buttonBox, SIGNAL(rejected()), dlgEditor, SLOT(reject())); 1572 buttonBox->button(QDialogButtonBox::Save)->setEnabled(false); 1573 1574 QLabel *lblFilepath = new QLabel(i18n("Editing file: %1", file)); 1575 QVBoxLayout *vlayout = new QVBoxLayout(dlgEditor); 1576 vlayout->addWidget(lblFilepath); 1577 vlayout->addWidget(textEdit); 1578 vlayout->addWidget(buttonBox); 1579 1580 // Read contents of unit file. 1581 QFile f(file); 1582 if (!f.open(QFile::ReadOnly | QFile::Text)) { 1583 displayMsgWidget(KMessageWidget::Error, 1584 i18n("Failed to open the unit file:\n%1", file)); 1585 return; 1586 } 1587 QTextStream in(&f); 1588 textEdit->setPlainText(in.readAll()); 1589 textEdit->setMinimumSize(500,300); 1590 1591 connect(textEdit, &QPlainTextEdit::textChanged, [=]{ 1592 buttonBox->button(QDialogButtonBox::Save)->setEnabled(true); 1593 }); 1594 1595 if (dlgEditor->exec() == QDialog::Accepted) { 1596 1597 // Declare a QVariantMap with arguments for the helper. 1598 QVariantMap helperArgs; 1599 helperArgs[QStringLiteral("file")] = file; 1600 helperArgs[QStringLiteral("contents")] = textEdit->toPlainText(); 1601 1602 // Create the action. 1603 KAuth::Action action(QStringLiteral("org.kde.kcontrol.systemdgenie.saveunitfile")); 1604 action.setHelperId(QStringLiteral("org.kde.kcontrol.systemdgenie")); 1605 action.setArguments(helperArgs); 1606 1607 KAuth::ExecuteJob *job = action.execute(); 1608 if (!job->exec()) { 1609 displayMsgWidget(KMessageWidget::Error, 1610 i18n("Unable to authenticate/execute the action: %1", job->error())); 1611 } else { 1612 //displayMsgWidget(KMessageWidget::Positive, 1613 // i18n("Unit file successfully written.")); 1614 m_lblLog->setText(i18n("%1: %2 successfully written...", QDateTime::currentDateTime().toString(QStringLiteral("hh:mm:ss")), file.section(QLatin1Char('/'), -1))); 1615 } 1616 } 1617 delete dlgEditor; 1618 } 1619 1620 QVector<SystemdUnit> MainWindow::getUnitsFromDbus(dbusBus bus) 1621 { 1622 // get an updated list of units via dbus 1623 1624 QVector<SystemdUnit> list; 1625 QVector<unitfile> unitfileslist; 1626 QDBusMessage dbusreply; 1627 1628 dbusreply = callDbusMethod(QStringLiteral("ListUnits"), sysdMgr, bus); 1629 1630 if (dbusreply.type() == QDBusMessage::ReplyMessage) 1631 { 1632 1633 const QDBusArgument argUnits = dbusreply.arguments().at(0).value<QDBusArgument>(); 1634 if (argUnits.currentType() == QDBusArgument::ArrayType) 1635 { 1636 argUnits.beginArray(); 1637 while (!argUnits.atEnd()) 1638 { 1639 SystemdUnit unit; 1640 argUnits >> unit; 1641 if (unit.following.isEmpty()) { 1642 list.append(unit); 1643 } 1644 } 1645 argUnits.endArray(); 1646 } 1647 //qDebug() << "Added " << list.size() << " units on bus " << bus; 1648 1649 // Get a list of unit files 1650 dbusreply = callDbusMethod(QStringLiteral("ListUnitFiles"), sysdMgr, bus); 1651 const QDBusArgument argUnitFiles = dbusreply.arguments().at(0).value<QDBusArgument>(); 1652 argUnitFiles.beginArray(); 1653 while (!argUnitFiles.atEnd()) 1654 { 1655 unitfile u; 1656 argUnitFiles.beginStructure(); 1657 argUnitFiles >> u.name >> u.status; 1658 argUnitFiles.endStructure(); 1659 unitfileslist.append(u); 1660 } 1661 argUnitFiles.endArray(); 1662 1663 // Add unloaded units to the list 1664 foreach (const unitfile &f, unitfileslist) 1665 { 1666 int index = list.indexOf(SystemdUnit(f.name.section(QLatin1Char('/'), -1))); 1667 if (index > -1) 1668 { 1669 // The unit was already in the list, add unit file and its status 1670 list[index].unit_file = f.name; 1671 list[index].unit_file_status = f.status; 1672 } 1673 else 1674 { 1675 // Unit not in the list, add it. 1676 QFile unitfile(f.name); 1677 if (unitfile.symLinkTarget().isEmpty()) 1678 { 1679 SystemdUnit unit; 1680 unit.id = f.name.section(QLatin1Char('/'), -1); 1681 unit.load_state = QStringLiteral("unloaded"); 1682 unit.active_state = QLatin1Char('-'); 1683 unit.sub_state = QLatin1Char('-'); 1684 unit.unit_file = f.name; 1685 unit.unit_file_status= f.status; 1686 list.append(unit); 1687 } 1688 } 1689 } 1690 // qDebug() << "Added " << tal << " units from files on bus " << bus; 1691 1692 } 1693 1694 return list; 1695 } 1696 1697 QVariant MainWindow::getDbusProperty(QString prop, dbusIface ifaceName, QDBusObjectPath path, dbusBus bus) 1698 { 1699 // qDebug() << "Fetching property" << prop << ifaceName << path.path() << "on bus" << bus; 1700 QString conn, ifc; 1701 QDBusConnection abus(m_systemBus); 1702 if (bus == user) 1703 abus = QDBusConnection::connectToBus(m_userBusPath, connSystemd); 1704 1705 if (ifaceName == sysdMgr) 1706 { 1707 conn = connSystemd; 1708 ifc = ifaceMgr; 1709 } 1710 else if (ifaceName == sysdUnit) 1711 { 1712 conn = connSystemd; 1713 ifc = ifaceUnit; 1714 } 1715 else if (ifaceName == sysdTimer) 1716 { 1717 conn = connSystemd; 1718 ifc = ifaceTimer; 1719 } 1720 else if (ifaceName == logdSession) 1721 { 1722 conn = connLogind; 1723 ifc = ifaceSession; 1724 } 1725 QVariant r; 1726 QDBusInterface *iface = new QDBusInterface (conn, path.path(), ifc, abus, this); 1727 if (iface->isValid()) 1728 { 1729 r = iface->property(prop.toLatin1()); 1730 delete iface; 1731 return r; 1732 } 1733 qDebug() << "Interface" << ifc << "invalid for" << path.path(); 1734 return QVariant(QStringLiteral("invalidIface")); 1735 } 1736 1737 QDBusMessage MainWindow::callDbusMethod(QString method, dbusIface ifaceName, dbusBus bus, const QList<QVariant> &args) 1738 { 1739 //qDebug() << "Calling method" << method << "with iface" << ifaceName << "on bus" << bus; 1740 1741 QDBusConnection abus(m_systemBus); 1742 if (bus == user) 1743 abus = QDBusConnection::connectToBus(m_userBusPath, connSystemd); 1744 1745 QDBusInterface *iface; 1746 if (ifaceName == sysdMgr) 1747 iface = new QDBusInterface(connSystemd, pathSysdMgr, ifaceMgr, abus, this); 1748 else if (ifaceName == logdMgr) 1749 iface = new QDBusInterface(connLogind, pathLogdMgr, ifaceLogdMgr, abus, this); 1750 1751 QDBusMessage msg; 1752 if (iface->isValid()) 1753 { 1754 if (args.isEmpty()) 1755 msg = iface->call(QDBus::AutoDetect, method); 1756 else 1757 msg = iface->callWithArgumentList(QDBus::AutoDetect, method, args); 1758 delete iface; 1759 if (msg.type() == QDBusMessage::ErrorMessage) 1760 qDebug() << "DBus method call failed: " << msg.errorMessage(); 1761 } 1762 else 1763 { 1764 qDebug() << "Invalid DBus interface on bus" << bus; 1765 delete iface; 1766 } 1767 return msg; 1768 } 1769 1770 void MainWindow::displayMsgWidget(KMessageWidget::MessageType type, QString msg) 1771 { 1772 KMessageWidget *msgWidget = new KMessageWidget; 1773 msgWidget->setText(msg); 1774 msgWidget->setMessageType(type); 1775 ui.verticalLayoutMsg->insertWidget(0, msgWidget); 1776 msgWidget->animatedShow(); 1777 } 1778