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

0001 /*
0002     SPDX-FileCopyrightText: 2007 Manolo Valdes <nolis71cu@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "process.h"
0008 #include "processes_local_p.h"
0009 
0010 #include <KLocalizedString>
0011 
0012 #include <QSet>
0013 
0014 #include <sys/param.h>
0015 #include <sys/resource.h>
0016 #include <sys/sysctl.h>
0017 #include <sys/types.h>
0018 #include <sys/user.h>
0019 #if defined(__DragonFly__)
0020 #include <err.h>
0021 #include <sys/resourcevar.h>
0022 #endif
0023 #include <signal.h>
0024 #include <stdlib.h>
0025 #include <unistd.h>
0026 
0027 namespace KSysGuard
0028 {
0029 class ProcessesLocal::Private
0030 {
0031 public:
0032     Private()
0033     {
0034         ;
0035     }
0036     ~Private()
0037     {
0038         ;
0039     }
0040     inline bool readProc(long pid, struct kinfo_proc *p);
0041     inline void readProcStatus(struct kinfo_proc *p, Process *process);
0042     inline void readProcStat(struct kinfo_proc *p, Process *process);
0043     inline void readProcStatm(struct kinfo_proc *p, Process *process);
0044     inline bool readProcCmdline(long pid, Process *process);
0045 };
0046 
0047 bool ProcessesLocal::Private::readProc(long pid, struct kinfo_proc *p)
0048 {
0049     int mib[4];
0050     size_t len;
0051 
0052     mib[0] = CTL_KERN;
0053     mib[1] = KERN_PROC;
0054     mib[2] = KERN_PROC_PID;
0055     mib[3] = pid;
0056 
0057     len = sizeof(struct kinfo_proc);
0058     if (sysctl(mib, 4, p, &len, NULL, 0) == -1 || !len)
0059         return false;
0060     return true;
0061 }
0062 
0063 void ProcessesLocal::Private::readProcStatus(struct kinfo_proc *p, Process *process)
0064 {
0065     process->setUid(0);
0066     process->setGid(0);
0067     process->setTracerpid(-1);
0068 
0069 #if defined(__FreeBSD__) && __FreeBSD_version >= 500015
0070     process->setUid(p->ki_uid);
0071     process->setGid(p->ki_pgid);
0072     process->setName(QString(p->ki_comm ? p->ki_comm : "????"));
0073 #elif defined(__DragonFly__) && __DragonFly_version >= 190000
0074     process->setUid(p->kp_uid);
0075     process->setGid(p->kp_pgid);
0076     process->setName(QString(p->kp_comm ? p->kp_comm : "????"));
0077 #else
0078     process->setUid(p->kp_eproc.e_ucred.cr_uid);
0079     process->setGid(p->kp_eproc.e_pgid);
0080 #endif
0081 }
0082 
0083 void ProcessesLocal::Private::readProcStat(struct kinfo_proc *p, Process *ps)
0084 {
0085     int status;
0086     struct rusage pru;
0087 #if defined(__FreeBSD__) && __FreeBSD_version >= 500015
0088     ps->setUserTime(p->ki_runtime / 10000);
0089     ps->setNiceLevel(p->ki_nice);
0090     ps->setVmSize(p->ki_size);
0091     ps->setVmRSS(p->ki_rssize * getpagesize());
0092     status = p->ki_stat;
0093 #elif defined(__DragonFly__) && __DragonFly_version >= 190000
0094     if (!getrusage(p->kp_pid, &pru)) {
0095         errx(1, "failed to get rusage info");
0096     }
0097     ps->setUserTime(pru.ru_utime.tv_usec / 1000); /*p_runtime / 1000*/
0098     ps->setNiceLevel(p->kp_nice);
0099     ps->setVmSize(p->kp_vm_map_size);
0100     ps->setVmRSS(p->kp_vm_rssize * getpagesize());
0101     status = p->kp_stat;
0102 #else
0103     ps->setUserTime(p->kp_proc.p_rtime.tv_sec * 100 + p->kp_proc.p_rtime.tv_usec / 100);
0104     ps->setNiceLevel(p->kp_proc.p_nice);
0105     ps->setVmSize(p->kp_eproc.e_vm.vm_map.size);
0106     ps->setVmRSS(p->kp_eproc.e_vm.vm_rssize * getpagesize());
0107     status = p->kp_proc.p_stat;
0108 #endif
0109     ps->setSysTime(0);
0110 
0111     // "idle","run","sleep","stop","zombie"
0112     switch (status) {
0113     case '0':
0114         ps->setStatus(Process::DiskSleep);
0115         break;
0116     case '1':
0117         ps->setStatus(Process::Running);
0118         break;
0119     case '2':
0120         ps->setStatus(Process::Sleeping);
0121         break;
0122     case '3':
0123         ps->setStatus(Process::Stopped);
0124         break;
0125     case '4':
0126         ps->setStatus(Process::Zombie);
0127         break;
0128     default:
0129         ps->setStatus(Process::OtherStatus);
0130         break;
0131     }
0132 }
0133 
0134 void ProcessesLocal::Private::readProcStatm(struct kinfo_proc *p, Process *process)
0135 {
0136     // TODO
0137 
0138     //     unsigned long shared;
0139     //     process->setVmURSS(process->vmRSS - (shared * sysconf(_SC_PAGESIZE) / 1024));
0140 }
0141 
0142 bool ProcessesLocal::Private::readProcCmdline(long pid, Process *process)
0143 {
0144     int mib[4];
0145     struct kinfo_proc p;
0146     size_t buflen = 256;
0147     char buf[256];
0148 
0149     mib[0] = CTL_KERN;
0150     mib[1] = KERN_PROC;
0151     mib[2] = KERN_PROC_ARGS;
0152     mib[3] = pid;
0153 
0154     if (sysctl(mib, 4, buf, &buflen, NULL, 0) == -1 || !buflen)
0155         return false;
0156     QString command = QString(buf);
0157 
0158     // cmdline separates parameters with the NULL character
0159     command.replace('\0', ' ');
0160     process->setCommand(command.trimmed());
0161 
0162     return true;
0163 }
0164 
0165 ProcessesLocal::ProcessesLocal()
0166     : d(new Private())
0167 {
0168 }
0169 
0170 long ProcessesLocal::getParentPid(long pid)
0171 {
0172     Q_ASSERT(pid != 0);
0173     long long ppid = -1;
0174     struct kinfo_proc p;
0175     if (d->readProc(pid, &p)) {
0176 #if defined(__FreeBSD__) && __FreeBSD_version >= 500015
0177         ppid = p.ki_ppid;
0178 #elif defined(__DragonFly__) && __DragonFly_version >= 190000
0179         ppid = p.kp_ppid;
0180 #else
0181         ppid = p.kp_eproc.e_ppid;
0182 #endif
0183     }
0184     return ppid;
0185 }
0186 
0187 bool ProcessesLocal::updateProcessInfo(long pid, Process *process)
0188 {
0189     struct kinfo_proc p;
0190     if (!d->readProc(pid, &p))
0191         return false;
0192     d->readProcStat(&p, process);
0193     d->readProcStatus(&p, process);
0194     d->readProcStatm(&p, process);
0195     if (!d->readProcCmdline(pid, process))
0196         return false;
0197 
0198     return true;
0199 }
0200 
0201 QSet<long> ProcessesLocal::getAllPids()
0202 {
0203     QSet<long> pids;
0204     int mib[3];
0205     size_t len;
0206     size_t num;
0207     struct kinfo_proc *p;
0208 
0209     mib[0] = CTL_KERN;
0210     mib[1] = KERN_PROC;
0211     mib[2] = KERN_PROC_ALL;
0212     sysctl(mib, 3, NULL, &len, NULL, 0);
0213     p = (kinfo_proc *)malloc(len);
0214     sysctl(mib, 3, p, &len, NULL, 0);
0215 
0216     for (num = 0; num < len / sizeof(struct kinfo_proc); num++)
0217 #if defined(__FreeBSD__) && __FreeBSD_version >= 500015
0218         pids.insert(p[num].ki_pid);
0219 #elif defined(__DragonFly__) && __DragonFly_version >= 190000
0220         pids.insert(p[num].kp_pid);
0221 #else
0222         pids.insert(p[num].kp_proc.p_pid);
0223 #endif
0224     free(p);
0225     return pids;
0226 }
0227 
0228 Processes::Error ProcessesLocal::sendSignal(long pid, int sig)
0229 {
0230     if (kill((pid_t)pid, sig)) {
0231         // Kill failed
0232         return Processes::Unknown;
0233     }
0234     return Processes::NoError;
0235 }
0236 
0237 Processes::Error ProcessesLocal::setNiceness(long pid, int priority)
0238 {
0239     if (setpriority(PRIO_PROCESS, pid, priority)) {
0240         // set niceness failed
0241         return Processes::Unknown;
0242     }
0243     return Processes::NoError;
0244 }
0245 
0246 Processes::Error ProcessesLocal::setScheduler(long pid, int priorityClass, int priority)
0247 {
0248     if (priorityClass == KSysGuard::Process::Other || priorityClass == KSysGuard::Process::Batch)
0249         priority = 0;
0250     if (pid <= 0)
0251         return Processes::InvalidPid; // check the parameters
0252     return Processes::NotSupported;
0253 }
0254 
0255 Processes::Error ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority)
0256 {
0257     return Processes::NotSupported; // Not yet supported
0258 }
0259 
0260 bool ProcessesLocal::supportsIoNiceness()
0261 {
0262     return false;
0263 }
0264 
0265 long long ProcessesLocal::totalPhysicalMemory()
0266 {
0267     static int physmem_mib[] = {CTL_HW, HW_PHYSMEM};
0268     /* get the page size with "getpagesize" and calculate pageshift from
0269      * it */
0270     int pagesize = ::getpagesize();
0271     int pageshift = 0;
0272     while (pagesize > 1) {
0273         pageshift++;
0274         pagesize >>= 1;
0275     }
0276     size_t Total = 0;
0277     size_t size = sizeof(Total);
0278     sysctl(physmem_mib, 2, &Total, &size, NULL, 0);
0279     return Total /= 1024;
0280 }
0281 
0282 long int KSysGuard::ProcessesLocal::numberProcessorCores()
0283 {
0284     int mib[2];
0285     int ncpu;
0286     size_t len;
0287 
0288     mib[0] = CTL_HW;
0289     mib[1] = HW_NCPU;
0290     len = sizeof(ncpu);
0291 
0292     if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1 || !len)
0293         return 1;
0294     return len;
0295 }
0296 ProcessesLocal::~ProcessesLocal()
0297 {
0298     delete d;
0299 }
0300 
0301 }