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

0001 /*
0002     SPDX-FileCopyrightText: 2007 Manolo Valdes <nolis71cu@gmail.com>
0003     SPDX-FileCopyrightText: 2010 Alex Hornung <ahornung@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "process.h"
0009 #include "processes_local_p.h"
0010 
0011 #include <KLocalizedString>
0012 
0013 #include <QSet>
0014 
0015 #include <err.h>
0016 #include <sched.h>
0017 #include <signal.h>
0018 #include <stdlib.h>
0019 #include <sys/param.h>
0020 #include <sys/resource.h>
0021 #include <sys/resourcevar.h>
0022 #include <sys/sysctl.h>
0023 #include <sys/types.h>
0024 #include <sys/user.h>
0025 #include <unistd.h>
0026 
0027 #define PP(pp, field) ((pp)->kp_##field)
0028 #define LP(pp, field) ((pp)->kp_lwp.kl_##field)
0029 #define VP(pp, field) ((pp)->kp_vm_##field)
0030 
0031 namespace KSysGuard
0032 {
0033 class ProcessesLocal::Private
0034 {
0035 public:
0036     Private()
0037     {
0038     }
0039     ~Private()
0040     {
0041     }
0042     inline bool readProc(long pid, struct kinfo_proc *p);
0043     inline void readProcStatus(struct kinfo_proc *p, Process *process);
0044     inline void readProcStat(struct kinfo_proc *p, Process *process);
0045     inline void readProcStatm(struct kinfo_proc *p, Process *process);
0046     inline bool readProcCmdline(long pid, Process *process);
0047 };
0048 
0049 bool ProcessesLocal::Private::readProc(long pid, struct kinfo_proc *p)
0050 {
0051     int mib[4];
0052     size_t len;
0053 
0054     mib[0] = CTL_KERN;
0055     mib[1] = KERN_PROC;
0056     mib[2] = KERN_PROC_PID;
0057     mib[3] = pid;
0058 
0059     len = sizeof(struct kinfo_proc);
0060     if (sysctl(mib, 4, p, &len, NULL, 0) == -1 || !len) {
0061         return false;
0062     }
0063     return true;
0064 }
0065 
0066 void ProcessesLocal::Private::readProcStatus(struct kinfo_proc *p, Process *process)
0067 {
0068     process->setUid(0);
0069     process->setGid(0);
0070     process->setTracerpid(-1);
0071 
0072     process->setEuid(PP(p, uid));
0073     process->setUid(PP(p, ruid));
0074     process->setEgid(PP(p, svgid));
0075     process->setGid(PP(p, rgid));
0076     process->setName(QString(PP(p, comm)));
0077 }
0078 
0079 void ProcessesLocal::Private::readProcStat(struct kinfo_proc *p, Process *ps)
0080 {
0081     ps->setUserTime(LP(p, uticks) / 10000);
0082     ps->setSysTime((LP(p, sticks) + LP(p, iticks)) / 10000);
0083     ps->setNiceLevel(PP(p, nice));
0084     ps->setVmSize(VP(p, map_size) / 1024); /* convert to KiB */
0085     ps->setVmRSS(VP(p, prssize) * getpagesize() / 1024); /* convert to KiB */
0086 
0087     // "idle","run","sleep","stop","zombie"
0088     switch (LP(p, stat)) {
0089     case LSRUN:
0090         ps->setStatus(Process::Running);
0091         break;
0092     case LSSLEEP:
0093         ps->setStatus(Process::Sleeping);
0094         break;
0095     case LSSTOP:
0096         ps->setStatus(Process::Stopped);
0097         break;
0098     default:
0099         ps->setStatus(Process::OtherStatus);
0100         break;
0101     }
0102     if (PP(p, stat) == SZOMB) {
0103         ps->setStatus(Process::Zombie);
0104     }
0105 }
0106 
0107 void ProcessesLocal::Private::readProcStatm(struct kinfo_proc *p, Process *process)
0108 {
0109     process->setVmURSS(-1);
0110 }
0111 
0112 bool ProcessesLocal::Private::readProcCmdline(long pid, Process *process)
0113 {
0114     int mib[4];
0115     size_t buflen = 256;
0116     char buf[256];
0117 
0118     mib[0] = CTL_KERN;
0119     mib[1] = KERN_PROC;
0120     mib[2] = KERN_PROC_ARGS;
0121     mib[3] = pid;
0122 
0123     if (sysctl(mib, 4, buf, &buflen, NULL, 0) == -1 || (buflen == 0)) {
0124         return false;
0125     }
0126     QString command = QString(buf);
0127 
0128     // cmdline separates parameters with the NULL character
0129     command.replace('\0', ' ');
0130     process->setCommand(command.trimmed());
0131 
0132     return true;
0133 }
0134 
0135 ProcessesLocal::ProcessesLocal()
0136     : d(new Private())
0137 {
0138 }
0139 
0140 long ProcessesLocal::getParentPid(long pid)
0141 {
0142     long long ppid = -1;
0143     struct kinfo_proc p;
0144 
0145     if (d->readProc(pid, &p)) {
0146         ppid = PP(&p, ppid);
0147     }
0148 
0149     return ppid;
0150 }
0151 
0152 bool ProcessesLocal::updateProcessInfo(long pid, Process *process)
0153 {
0154     struct kinfo_proc p;
0155 
0156     if (!d->readProc(pid, &p)) {
0157         return false;
0158     }
0159 
0160     d->readProcStat(&p, process);
0161     d->readProcStatus(&p, process);
0162     d->readProcStatm(&p, process);
0163     if (!d->readProcCmdline(pid, process)) {
0164         return false;
0165     }
0166 
0167     return true;
0168 }
0169 
0170 QSet<long> ProcessesLocal::getAllPids()
0171 {
0172     QSet<long> pids;
0173     int mib[3];
0174     size_t len;
0175     size_t num;
0176     struct kinfo_proc *p;
0177 
0178     mib[0] = CTL_KERN;
0179     mib[1] = KERN_PROC;
0180     mib[2] = KERN_PROC_ALL;
0181     if (sysctl(mib, 3, NULL, &len, NULL, 0) == -1) {
0182         return pids;
0183     }
0184     if ((p = (kinfo_proc *)malloc(len)) == NULL) {
0185         return pids;
0186     }
0187     if (sysctl(mib, 3, p, &len, NULL, 0) == -1) {
0188         free(p);
0189         return pids;
0190     }
0191 
0192     for (num = 0; num < len / sizeof(struct kinfo_proc); num++) {
0193         long pid = PP((&p[num]), pid);
0194         long long ppid = PP((&p[num]), ppid);
0195 
0196         // skip all process with parent id = 0 but init
0197         if (ppid <= 0 && pid != 1) {
0198             continue;
0199         }
0200         pids.insert(pid);
0201     }
0202     free(p);
0203     return pids;
0204 }
0205 
0206 Processes::Error ProcessesLocal::sendSignal(long pid, int sig)
0207 {
0208     if (kill((pid_t)pid, sig)) {
0209         // Kill failed
0210         return Processes::Unknown;
0211     }
0212     return Processes::NoError;
0213 }
0214 
0215 Processes::Error ProcessesLocal::setNiceness(long pid, int priority)
0216 {
0217     if (setpriority(PRIO_PROCESS, pid, priority)) {
0218         // set niceness failed
0219         return Processes::Unknown;
0220     }
0221     return Processes::NoError;
0222 }
0223 
0224 Processes::Error ProcessesLocal::setScheduler(long pid, int priorityClass, int priority)
0225 {
0226     if (priorityClass == KSysGuard::Process::Other || priorityClass == KSysGuard::Process::Batch) {
0227         priority = 0;
0228     }
0229     if (pid <= 0) {
0230         return Processes::InvalidPid; // check the parameters
0231     }
0232     struct sched_param params;
0233     params.sched_priority = priority;
0234     bool success;
0235     switch (priorityClass) {
0236     case (KSysGuard::Process::Other):
0237         success = (sched_setscheduler(pid, SCHED_OTHER, &params) == 0);
0238         break;
0239     case (KSysGuard::Process::RoundRobin):
0240         success = (sched_setscheduler(pid, SCHED_RR, &params) == 0);
0241         break;
0242     case (KSysGuard::Process::Fifo):
0243         success = (sched_setscheduler(pid, SCHED_FIFO, &params) == 0);
0244         break;
0245 #ifdef SCHED_BATCH
0246     case (KSysGuard::Process::Batch):
0247         success = (sched_setscheduler(pid, SCHED_BATCH, &params) == 0);
0248         break;
0249 #endif
0250     }
0251     if (success) {
0252         return Processes::NoError;
0253     }
0254     return Processes::Unknown;
0255 }
0256 
0257 Processes::Error ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority)
0258 {
0259     return Processes::NotSupported; // Not yet supported
0260 }
0261 
0262 bool ProcessesLocal::supportsIoNiceness()
0263 {
0264     return false;
0265 }
0266 
0267 long long ProcessesLocal::totalPhysicalMemory()
0268 {
0269     size_t Total;
0270     size_t len;
0271 
0272     len = sizeof(Total);
0273     if (sysctlbyname("hw.physmem", &Total, &len, NULL, 0) == -1) {
0274         return 0;
0275     }
0276 
0277     Total *= getpagesize() / 1024;
0278     return Total;
0279 }
0280 
0281 ProcessesLocal::~ProcessesLocal()
0282 {
0283     delete d;
0284 }
0285 
0286 }