File indexing completed on 2024-04-28 16:49:56

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     return true;
0063 }
0064 
0065 void ProcessesLocal::Private::readProcStatus(struct kinfo_proc *p, Process *process)
0066 {
0067     process->setUid(0);
0068     process->setGid(0);
0069     process->setTracerpid(-1);
0070 
0071     process->setEuid(PP(p, uid));
0072     process->setUid(PP(p, ruid));
0073     process->setEgid(PP(p, svgid));
0074     process->setGid(PP(p, rgid));
0075     process->setName(QString(PP(p, comm)));
0076 }
0077 
0078 void ProcessesLocal::Private::readProcStat(struct kinfo_proc *p, Process *ps)
0079 {
0080     ps->setUserTime(LP(p, uticks) / 10000);
0081     ps->setSysTime((LP(p, sticks) + LP(p, iticks)) / 10000);
0082     ps->setNiceLevel(PP(p, nice));
0083     ps->setVmSize(VP(p, map_size) / 1024); /* convert to KiB */
0084     ps->setVmRSS(VP(p, prssize) * getpagesize() / 1024); /* convert to KiB */
0085 
0086     // "idle","run","sleep","stop","zombie"
0087     switch (LP(p, stat)) {
0088     case LSRUN:
0089         ps->setStatus(Process::Running);
0090         break;
0091     case LSSLEEP:
0092         ps->setStatus(Process::Sleeping);
0093         break;
0094     case LSSTOP:
0095         ps->setStatus(Process::Stopped);
0096         break;
0097     default:
0098         ps->setStatus(Process::OtherStatus);
0099         break;
0100     }
0101     if (PP(p, stat) == SZOMB)
0102         ps->setStatus(Process::Zombie);
0103 }
0104 
0105 void ProcessesLocal::Private::readProcStatm(struct kinfo_proc *p, Process *process)
0106 {
0107     process->setVmURSS(-1);
0108 }
0109 
0110 bool ProcessesLocal::Private::readProcCmdline(long pid, Process *process)
0111 {
0112     int mib[4];
0113     size_t buflen = 256;
0114     char buf[256];
0115 
0116     mib[0] = CTL_KERN;
0117     mib[1] = KERN_PROC;
0118     mib[2] = KERN_PROC_ARGS;
0119     mib[3] = pid;
0120 
0121     if (sysctl(mib, 4, buf, &buflen, NULL, 0) == -1 || (buflen == 0))
0122         return false;
0123     QString command = QString(buf);
0124 
0125     // cmdline separates parameters with the NULL character
0126     command.replace('\0', ' ');
0127     process->setCommand(command.trimmed());
0128 
0129     return true;
0130 }
0131 
0132 ProcessesLocal::ProcessesLocal()
0133     : d(new Private())
0134 {
0135 }
0136 
0137 long ProcessesLocal::getParentPid(long pid)
0138 {
0139     long long ppid = -1;
0140     struct kinfo_proc p;
0141 
0142     if (d->readProc(pid, &p))
0143         ppid = PP(&p, ppid);
0144 
0145     return ppid;
0146 }
0147 
0148 bool ProcessesLocal::updateProcessInfo(long pid, Process *process)
0149 {
0150     struct kinfo_proc p;
0151 
0152     if (!d->readProc(pid, &p)) {
0153         return false;
0154     }
0155 
0156     d->readProcStat(&p, process);
0157     d->readProcStatus(&p, process);
0158     d->readProcStatm(&p, process);
0159     if (!d->readProcCmdline(pid, process)) {
0160         return false;
0161     }
0162 
0163     return true;
0164 }
0165 
0166 QSet<long> ProcessesLocal::getAllPids()
0167 {
0168     QSet<long> pids;
0169     int mib[3];
0170     size_t len;
0171     size_t num;
0172     struct kinfo_proc *p;
0173 
0174     mib[0] = CTL_KERN;
0175     mib[1] = KERN_PROC;
0176     mib[2] = KERN_PROC_ALL;
0177     if (sysctl(mib, 3, NULL, &len, NULL, 0) == -1)
0178         return pids;
0179     if ((p = (kinfo_proc *)malloc(len)) == NULL)
0180         return pids;
0181     if (sysctl(mib, 3, p, &len, NULL, 0) == -1) {
0182         free(p);
0183         return pids;
0184     }
0185 
0186     for (num = 0; num < len / sizeof(struct kinfo_proc); num++) {
0187         long pid = PP((&p[num]), pid);
0188         long long ppid = PP((&p[num]), ppid);
0189 
0190         // skip all process with parent id = 0 but init
0191         if (ppid <= 0 && pid != 1)
0192             continue;
0193         pids.insert(pid);
0194     }
0195     free(p);
0196     return pids;
0197 }
0198 
0199 Processes::Error ProcessesLocal::sendSignal(long pid, int sig)
0200 {
0201     if (kill((pid_t)pid, sig)) {
0202         // Kill failed
0203         return Processes::Unknown;
0204     }
0205     return Processes::NoError;
0206 }
0207 
0208 Processes::Error ProcessesLocal::setNiceness(long pid, int priority)
0209 {
0210     if (setpriority(PRIO_PROCESS, pid, priority)) {
0211         // set niceness failed
0212         return Processes::Unknown;
0213     }
0214     return Processes::NoError;
0215 }
0216 
0217 Processes::Error ProcessesLocal::setScheduler(long pid, int priorityClass, int priority)
0218 {
0219     if (priorityClass == KSysGuard::Process::Other || priorityClass == KSysGuard::Process::Batch)
0220         priority = 0;
0221     if (pid <= 0)
0222         return Processes::InvalidPid; // check the parameters
0223     struct sched_param params;
0224     params.sched_priority = priority;
0225     bool success;
0226     switch (priorityClass) {
0227     case (KSysGuard::Process::Other):
0228         success = (sched_setscheduler(pid, SCHED_OTHER, &params) == 0);
0229         break;
0230     case (KSysGuard::Process::RoundRobin):
0231         success = (sched_setscheduler(pid, SCHED_RR, &params) == 0);
0232         break;
0233     case (KSysGuard::Process::Fifo):
0234         success = (sched_setscheduler(pid, SCHED_FIFO, &params) == 0);
0235         break;
0236 #ifdef SCHED_BATCH
0237     case (KSysGuard::Process::Batch):
0238         success = (sched_setscheduler(pid, SCHED_BATCH, &params) == 0);
0239         break;
0240 #endif
0241     }
0242     if (success) {
0243         return Processes::NoError;
0244     }
0245     return Processes::Unknown;
0246 }
0247 
0248 Processes::Error ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority)
0249 {
0250     return Processes::NotSupported; // Not yet supported
0251 }
0252 
0253 bool ProcessesLocal::supportsIoNiceness()
0254 {
0255     return false;
0256 }
0257 
0258 long long ProcessesLocal::totalPhysicalMemory()
0259 {
0260     size_t Total;
0261     size_t len;
0262 
0263     len = sizeof(Total);
0264     if (sysctlbyname("hw.physmem", &Total, &len, NULL, 0) == -1)
0265         return 0;
0266 
0267     Total *= getpagesize() / 1024;
0268     return Total;
0269 }
0270 
0271 ProcessesLocal::~ProcessesLocal()
0272 {
0273     delete d;
0274 }
0275 
0276 }