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

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 #ifndef PROCESSMODEL_P_H_
0010 #define PROCESSMODEL_P_H_
0011 
0012 #include "ProcessModel.h"
0013 #include <processcore/extended_process_list.h>
0014 
0015 #include "../config-ksysguard.h"
0016 #include <QDebug>
0017 #include <QHash>
0018 #include <QList>
0019 #include <QObject>
0020 #include <QPixmap>
0021 #include <QSet>
0022 #include <QTime>
0023 #include <QVariant>
0024 #include <kuser.h>
0025 
0026 #if HAVE_X11
0027 #include <X11/Xatom.h>
0028 #include <X11/Xlib.h>
0029 #include <fixx11h.h>
0030 #include <kwindowsystem.h>
0031 #include <netwm.h>
0032 #include <private/qtx11extras_p.h>
0033 
0034 struct WindowInfo {
0035     WindowInfo(WId _wid, qlonglong _pid)
0036         : wid(_wid)
0037         , pid(_pid)
0038     {
0039     }
0040 
0041     qlonglong pid;
0042     QPixmap icon;
0043     WId wid;
0044     QString name;
0045 };
0046 #endif
0047 
0048 namespace KSysGuard
0049 {
0050 class Processes;
0051 }
0052 
0053 class ProcessModelPrivate : public QObject
0054 {
0055     Q_OBJECT
0056 public:
0057     ProcessModelPrivate();
0058     ~ProcessModelPrivate() override;
0059 public Q_SLOTS:
0060 
0061 #if HAVE_X11
0062     /** When an X window is changed, this is called */
0063     void windowChanged(WId wid, NET::Properties properties, NET::Properties2 properties2);
0064     /** When an X window is created, this is called
0065      */
0066     void windowAdded(WId wid);
0067     /** When an X window is closed, this is called
0068      */
0069     void windowRemoved(WId wid);
0070 #endif
0071 
0072     /** Change the data for a process.  This is called from KSysGuard::Processes
0073      *  if @p onlyCpuOrMem is set, only the total cpu usage is updated.
0074      *  process->changes  contains a bitfield of what has been changed
0075      */
0076     void processChanged(KSysGuard::Process *process, bool onlyCpuOrMem);
0077     /** Called from KSysGuard::Processes
0078      *  This indicates we are about to insert a process in the model.  Emit the appropriate signals
0079      */
0080     void beginInsertRow(KSysGuard::Process *parent);
0081     /** Called from KSysGuard::Processes
0082      *  We have finished inserting a process
0083      */
0084     void endInsertRow();
0085     /** Called from KSysGuard::Processes
0086      *  This indicates we are about to remove a process in the model.  Emit the appropriate signals
0087      */
0088     void beginRemoveRow(KSysGuard::Process *process);
0089     /** Called from KSysGuard::Processes
0090      *  We have finished removing a process
0091      */
0092     void endRemoveRow();
0093     /** Called from KSysGuard::Processes
0094      *  This indicates we are about to move a process in the model from one parent process to another.  Emit the appropriate signals
0095      */
0096     void beginMoveProcess(KSysGuard::Process *process, KSysGuard::Process *new_parent);
0097     /** Called from KSysGuard::Processes
0098      *  We have finished moving a process
0099      */
0100     void endMoveRow();
0101 
0102 public:
0103     /** Connects to the host */
0104     void setupProcesses();
0105     /** A mapping of running,stopped,etc  to a friendly description like 'Stopped, either by a job control signal or because it is being traced.'*/
0106     QString getStatusDescription(KSysGuard::Process::ProcessStatus status) const;
0107 
0108     /** Return a qt markup tooltip string for a local user.  It will have their full name etc.
0109      *  This will be slow the first time, as it practically indirectly reads the whole of /etc/passwd
0110      *  But the second time will be as fast as hash lookup as we cache the result
0111      */
0112     inline QString getTooltipForUser(const KSysGuard::Process *process) const;
0113 
0114     /** Return a username for a local user if it can, otherwise just their uid.
0115      *  This may have been given from the result of "ps" (but not necessarily).
0116      *  If it's not found, then it needs to find out the username from the uid.
0117      *  This will be slow the first time, as it practically indirectly reads the whole of /etc/passwd
0118      *  But the second time will be as fast as hash lookup as we cache the result
0119      *
0120      *  If withuid is set, and the username is found, return: "username (Uid: uid)"
0121      */
0122     inline QString getUsernameForUser(long uid, bool withuid) const;
0123 
0124     /** Return the groupname for a given gid.  This is in the form of "gid" if not known, or
0125      *  "groupname (Uid: gid)" if known.
0126      */
0127     inline QString getGroupnameForGroup(long gid) const;
0128 #if HAVE_X11
0129     /** On X11 system, connects to the signals emitted when windows are created/destroyed */
0130     void setupWindows();
0131     void updateWindowInfo(WId wid, NET::Properties properties, NET::Properties2 properties2, bool newWindow);
0132     QMultiHash<long long, WindowInfo *> mPidToWindowInfo; ///< Map a process pid to X window info if available
0133     QHash<WId, WindowInfo *> mWIdToWindowInfo; ///< Map an X window id to window info
0134 #if HAVE_XRES
0135     bool updateXResClientData();
0136     void queryForAndUpdateAllXWindows();
0137 #endif
0138 #endif
0139     void timerEvent(QTimerEvent *event) override; ///< Call dataChanged() for all the processes in mPidsToUpdate
0140     /** @see setIsLocalhost */
0141     bool mIsLocalhost;
0142 
0143     /** A caching hash for tooltips for a user.
0144      *  @see getTooltipForUser */
0145     mutable QHash<long long, QString> mUserTooltips;
0146 
0147     /** A caching hash for username for a user uid, or just their uid if it can't be found (as a long long)
0148      *  @see getUsernameForUser */
0149     mutable QHash<long long, QString> mUserUsername;
0150 
0151     /** A mapping of a user id to whether this user can log in.  We have to guess based on the shell.
0152      *  All are set to true to non localhost.
0153      *  It is set to:
0154      *    0 if the user cannot login
0155      *    1 is the user can login
0156      *  The reason for using an int and not a bool is so that we can do
0157      *  \code mUidCanLogin.value(uid,-1) \endcode  and thus we get a tristate for whether
0158      *  they are logged in, not logged in, or not known yet.
0159      *  */
0160     mutable QHash<long long, int> mUidCanLogin;
0161 
0162     /** A translated list of headings (column titles) in the order we want to display them. Used in headerData() */
0163     QStringList mHeadings;
0164 
0165     bool mShowChildTotals; ///< If set to true, a parent will return the CPU usage of all its children recursively
0166 
0167     bool mSimple; //< In simple mode, the model returns everything as flat, with no icons, etc.  This is set by changing cmbFilter
0168 
0169     QTime mLastUpdated; ///< Time that we last updated the processes.
0170 
0171     long long mMemTotal; ///< the total amount of physical memory in kb in the machine.  We can used this to determine the percentage of memory an app is using
0172     int mNumProcessorCores; ///< The number of (enabled) processor cores in the this machine
0173 
0174     QSharedPointer<KSysGuard::ExtendedProcesses> mProcesses; ///< The processes instance
0175 
0176     QPixmap mBlankPixmap; ///< Used to pad out process names which don't have an icon
0177 
0178     /** Show the process command line options in the process name column */
0179     bool mShowCommandLineOptions;
0180 
0181     bool mShowingTooltips;
0182     bool mNormalizeCPUUsage;
0183     /** When displaying memory sizes, this is the units it should be displayed in */
0184     ProcessModel::Units mUnits;
0185     ProcessModel::Units mIoUnits;
0186 
0187     ProcessModel::IoInformation mIoInformation;
0188 
0189     /** The hostname */
0190     QString mHostName;
0191     bool mHaveTimer;
0192     int mTimerId;
0193     QList<long> mPidsToUpdate; ///< A list of pids that we need to emit dataChanged() for regularly
0194 
0195     static const int MAX_HIST_ENTRIES = 100;
0196     static const int MIN_HIST_AGE = 200; ///< If the latest history entry is at least this ms old, a new one gets added
0197     /** Storage for the history entries. We need one per percentage column. */
0198     QHash<KSysGuard::Process *, QList<ProcessModel::PercentageHistoryEntry>> mMapProcessCPUHistory;
0199 
0200     QList<KSysGuard::ProcessAttribute *> mExtraAttributes;
0201 
0202 #if HAVE_XRES
0203     bool mHaveXRes; ///< True if the XRes extension is available at run time
0204     QMap<qlonglong, XID> mXResClientResources;
0205 #endif
0206 
0207     bool mMovingRow;
0208     bool mRemovingRow;
0209     bool mInsertingRow;
0210 
0211     bool mIsX11;
0212 
0213     ProcessModel *q;
0214 };
0215 
0216 #endif