File indexing completed on 2024-04-28 05:31:42

0001 /*
0002     KSysGuard, the KDE System Guard
0003 
0004     SPDX-FileCopyrightText: 2006-2007 John Tapsell <john.tapsell@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 
0008 */
0009 #include "processui_debug.h"
0010 
0011 /* For getuid() */
0012 #include <sys/types.h>
0013 #include <unistd.h>
0014 
0015 #include <QDebug>
0016 #include <QVariant>
0017 
0018 #include "ProcessFilter.h"
0019 #include "ProcessModel.h"
0020 #include "ProcessModel_p.h"
0021 
0022 bool ProcessFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
0023 {
0024     if ((mFilter == AllProcesses || mFilter == AllProcessesInTreeForm) && filterRegularExpression().pattern().isEmpty()) {
0025         return true; // Shortcut for common case
0026     }
0027 
0028     ProcessModel *model = static_cast<ProcessModel *>(sourceModel());
0029     const KSysGuard::Process *process;
0030     if (model->isSimpleMode()) {
0031         if (source_parent.isValid()) {
0032             qCDebug(LIBKSYSGUARD_PROCESSUI) << "Serious error with data.  In simple mode, there should be no children";
0033             return true;
0034         }
0035         process = model->getProcessAtIndex(source_row);
0036     } else {
0037         KSysGuard::Process *parent_process = nullptr;
0038         if (source_parent.isValid()) {
0039             parent_process = reinterpret_cast<KSysGuard::Process *>(source_parent.internalPointer());
0040             Q_ASSERT(parent_process);
0041         } else {
0042             // if(!model->isSimpleMode()) {
0043             parent_process = model->getProcess(-1); // Get our 'special' process which should have the root init child
0044             Q_ASSERT(parent_process);
0045             //}
0046         }
0047         if (!model->isSimpleMode() && source_row >= parent_process->children().size()) {
0048             qCDebug(LIBKSYSGUARD_PROCESSUI) << "Serious error with data.  Source row requested for a non existent row. Requested " << source_row << " of "
0049                                             << parent_process->children().size() << " for " << parent_process->pid();
0050             return true;
0051         }
0052 
0053         process = parent_process->children().at(source_row);
0054     }
0055     Q_ASSERT(process);
0056     long uid = process->uid();
0057     long euid = process->euid();
0058 
0059     bool accepted = true;
0060     switch (mFilter) {
0061     case AllProcesses:
0062     case AllProcessesInTreeForm:
0063         break;
0064     case SystemProcesses:
0065         if (uid >= 100 && model->canUserLogin(uid)) {
0066             accepted = false;
0067         }
0068         break;
0069     case UserProcesses:
0070         if ((uid < 100 || !model->canUserLogin(uid)) && (euid < 100 || !model->canUserLogin(euid))) {
0071             accepted = false;
0072         }
0073         break;
0074     case OwnProcesses: {
0075         long ownuid = getuid();
0076         if (uid != ownuid && process->suid() != ownuid && process->fsuid() != ownuid && euid != ownuid) {
0077             accepted = false;
0078         }
0079         break;
0080     }
0081     case ProgramsOnly:
0082         if (process->tty().isEmpty()) {
0083             if (!model->hasGUIWindow(process->pid())) {
0084                 accepted = false;
0085             }
0086         } else {
0087             // login and getty kinda _are_ the tty, so I do not really count them as 'programs'. So make a special case and hide them
0088             // Their ppid are 1 (init) so by checking we try to avoid false matches, and speed up checking overall
0089             QString name = process->name().section(QLatin1Char(' '), 0, 0);
0090             if (process->parentPid() == 1 && (name == QLatin1String("login") || name.endsWith(QLatin1String("getty")))) {
0091                 accepted = false;
0092             }
0093         }
0094         break;
0095     default:
0096         break;
0097     }
0098 
0099     if (accepted) {
0100         if (filterRegularExpression().pattern().isEmpty()) {
0101             return true;
0102         }
0103 
0104         // Allow the user to search by PID
0105         if (QString::number(process->pid()).contains(filterRegularExpression())) {
0106             return true;
0107         }
0108         // None of our tests have rejected it.  Pass it on to qsortfilterproxymodel's filter
0109         if (QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent)) {
0110             return true;
0111         }
0112 
0113         auto strings = filterRegularExpression().pattern().split(QLatin1Char(','), Qt::SkipEmptyParts);
0114         for (auto string : strings) {
0115             string = string.trimmed();
0116             if (process->name().indexOf(string) != -1 || QString::number(process->pid()).indexOf(string) != -1) {
0117                 return true;
0118             }
0119         }
0120     }
0121 
0122     // We did not accept this row at all.
0123 
0124     // If we are in flat mode, then give up now
0125     if (mFilter != AllProcessesInTreeForm) {
0126         return false;
0127     }
0128 
0129     // one of our children might be accepted, so accept this row if our children are accepted.
0130     QModelIndex source_index = sourceModel()->index(source_row, 0, source_parent);
0131     for (int i = 0; i < sourceModel()->rowCount(source_index); i++) {
0132         if (filterAcceptsRow(i, source_index)) {
0133             return true;
0134         }
0135     }
0136     return false;
0137 }
0138 
0139 bool ProcessFilter::lessThan(const QModelIndex &left, const QModelIndex &right) const
0140 {
0141     if (right.isValid() && left.isValid()) {
0142         Q_ASSERT(left.model());
0143         Q_ASSERT(right.model());
0144         const ProcessModel *model = static_cast<const ProcessModel *>(left.model());
0145         return model->lessThan(left, right);
0146     }
0147     return QSortFilterProxyModel::lessThan(left, right);
0148 }
0149 
0150 void ProcessFilter::setFilter(State filter)
0151 {
0152     mFilter = filter;
0153     invalidateFilter(); // Tell the proxy view to refresh all its information
0154 }