1 /*
2     SPDX-FileCopyrightText: 2007 Manolo Valdes <nolis71cu@gmail.com>
3     SPDX-FileCopyrightText: 2007 Mark Davies <mark@mcs.vuw.ac.nz>
4 
5     SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "process.h"
9 #include "processes_local_p.h"
10 
11 #include <KLocalizedString>
12 
13 #include <QSet>
14 
15 #include <kvm.h>
16 #include <sched.h>
17 #include <signal.h>
18 #include <stdlib.h>
19 #include <sys/param.h>
20 #include <sys/stat.h>
21 #include <sys/sysctl.h>
22 #include <sys/types.h>
23 #include <sys/user.h>
24 #include <unistd.h>
25 
26 namespace KSysGuard
27 {
28 class ProcessesLocal::Private
29 {
30 public:
Private()31     Private()
32     {
33         kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
34     }
~Private()35     ~Private()
36     {
37         kvm_close(kd);
38     }
39     inline bool readProc(long pid, struct kinfo_proc2 **p, int *num);
40     inline void readProcStatus(struct kinfo_proc2 *p, Process *process);
41     inline void readProcStat(struct kinfo_proc2 *p, Process *process);
42     inline void readProcStatm(struct kinfo_proc2 *p, Process *process);
43     inline bool readProcCmdline(struct kinfo_proc2 *p, Process *process);
44 
45     kvm_t *kd;
46 };
47 
48 #ifndef _SC_NPROCESSORS_ONLN
numberProcessorCores()49 long int KSysGuard::ProcessesLocal::numberProcessorCores()
50 {
51     int mib[2];
52     int ncpu;
53     size_t len;
54 
55     mib[0] = CTL_HW;
56     mib[1] = HW_NCPU;
57     len = sizeof(ncpu);
58 
59     if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1 || !len)
60         return 1;
61     return len;
62 }
63 #endif
64 
readProc(long pid,struct kinfo_proc2 ** p,int * num)65 bool ProcessesLocal::Private::readProc(long pid, struct kinfo_proc2 **p, int *num)
66 {
67     int len;
68     int op, arg;
69 
70     if (pid == 0) {
71         op = KERN_PROC_ALL;
72         arg = 0;
73     } else {
74         op = KERN_PROC_PID;
75         arg = pid;
76     }
77     *p = kvm_getproc2(kd, op, arg, sizeof(struct kinfo_proc2), &len);
78 
79     if (len < 1)
80         return false;
81 
82     if (num != NULL)
83         *num = len;
84     return true;
85 }
86 
readProcStatus(struct kinfo_proc2 * p,Process * process)87 void ProcessesLocal::Private::readProcStatus(struct kinfo_proc2 *p, Process *process)
88 {
89     process->setUid(p->p_ruid);
90     process->setEuid(p->p_uid);
91     process->setGid(p->p_rgid);
92     process->setEgid(p->p_gid);
93     process->setTracerpid(-1);
94 
95     process->setName(QString(p->p_comm ? p->p_comm : "????"));
96 }
97 
readProcStat(struct kinfo_proc2 * p,Process * ps)98 void ProcessesLocal::Private::readProcStat(struct kinfo_proc2 *p, Process *ps)
99 {
100     const char *ttname;
101     dev_t dev;
102 
103     ps->setUserTime(p->p_uutime_sec * 100 + p->p_uutime_usec / 10000);
104     ps->setSysTime(p->p_ustime_sec * 100 + p->p_ustime_usec / 10000);
105 
106     ps->setUserUsage(100.0 * ((double)(p->p_pctcpu) / FSCALE));
107     ps->setSysUsage(0);
108 
109     ps->setNiceLevel(p->p_nice - NZERO);
110     ps->setVmSize((p->p_vm_tsize + p->p_vm_dsize + p->p_vm_ssize) * getpagesize());
111     ps->setVmRSS(p->p_vm_rssize * getpagesize());
112 
113     // "idle","run","sleep","stop","zombie"
114     switch (p->p_stat) {
115     case LSRUN:
116         ps->setStatus(Process::Running);
117         break;
118     case LSSLEEP:
119         ps->setStatus(Process::Sleeping);
120         break;
121     case LSSTOP:
122         ps->setStatus(Process::Stopped);
123         break;
124     case LSZOMB:
125         ps->setStatus(Process::Zombie);
126         break;
127     case LSONPROC:
128         ps->setStatus(Process::Running);
129         break;
130     default:
131         ps->setStatus(Process::OtherStatus);
132         break;
133     }
134 
135     dev = p->p_tdev;
136     if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) {
137         ps->setTty(QByteArray());
138     } else {
139         ps->setTty(QByteArray(ttname));
140     }
141 }
142 
readProcStatm(struct kinfo_proc2 * p,Process * process)143 void ProcessesLocal::Private::readProcStatm(struct kinfo_proc2 *p, Process *process)
144 {
145     // TODO
146 
147     //     unsigned long shared;
148     //     process->vmURSS = process->vmRSS - (shared * sysconf(_SC_PAGESIZE) / 1024);
149     process->setVmURSS(-1);
150 }
151 
readProcCmdline(struct kinfo_proc2 * p,Process * process)152 bool ProcessesLocal::Private::readProcCmdline(struct kinfo_proc2 *p, Process *process)
153 {
154     char **argv;
155 
156     if ((argv = kvm_getargv2(kd, p, 256)) == NULL)
157         return false;
158 
159     QString command = QString("");
160 
161     while (*argv) {
162         command += *argv;
163         command += " ";
164         argv++;
165     }
166     process->setCommand(command.trimmed());
167 
168     return true;
169 }
170 
ProcessesLocal()171 ProcessesLocal::ProcessesLocal()
172     : d(new Private())
173 {
174 }
175 
getParentPid(long pid)176 long ProcessesLocal::getParentPid(long pid)
177 {
178     long long ppid = -1;
179     struct kinfo_proc2 *p;
180     if (d->readProc(pid, &p, 0)) {
181         ppid = p->p_ppid;
182     }
183     return ppid;
184 }
185 
updateProcessInfo(long pid,Process * process)186 bool ProcessesLocal::updateProcessInfo(long pid, Process *process)
187 {
188     struct kinfo_proc2 *p;
189     if (!d->readProc(pid, &p, NULL))
190         return false;
191     d->readProcStat(p, process);
192     d->readProcStatus(p, process);
193     d->readProcStatm(p, process);
194     if (!d->readProcCmdline(p, process))
195         return false;
196 
197     return true;
198 }
199 
getAllPids()200 QSet<long> ProcessesLocal::getAllPids()
201 {
202     QSet<long> pids;
203     int len;
204     int num;
205     struct kinfo_proc2 *p;
206 
207     d->readProc(0, &p, &len);
208 
209     for (num = 0; num < len; num++) {
210         long pid = p[num].p_pid;
211         long long ppid = p[num].p_ppid;
212 
213         // skip all process with parent id = 0 but init
214         if (ppid <= 0 && pid != 1)
215             continue;
216         pids.insert(pid);
217     }
218     return pids;
219 }
220 
sendSignal(long pid,int sig)221 Processes::Error ProcessesLocal::sendSignal(long pid, int sig)
222 {
223     if (kill((pid_t)pid, sig)) {
224         // Kill failed
225         return Processes::Unknown;
226     }
227     return Processes::NoError;
228 }
229 
setNiceness(long pid,int priority)230 Processes::Error ProcessesLocal::setNiceness(long pid, int priority)
231 {
232     if (setpriority(PRIO_PROCESS, pid, priority)) {
233         // set niceness failed
234         return Processes::Unknown;
235     }
236     return Processes::NoError;
237 }
238 
setScheduler(long pid,int priorityClass,int priority)239 Processes::Error ProcessesLocal::setScheduler(long pid, int priorityClass, int priority)
240 {
241     if (priorityClass == KSysGuard::Process::Other || priorityClass == KSysGuard::Process::Batch)
242         priority = 0;
243     if (pid <= 0)
244         return Processes::InvalidPid; // check the parameters
245     struct sched_param params;
246     params.sched_priority = priority;
247     bool success;
248     switch (priorityClass) {
249     case (KSysGuard::Process::Other):
250         success = (sched_setscheduler(pid, SCHED_OTHER, &params) == 0);
251         break;
252     case (KSysGuard::Process::RoundRobin):
253         success = (sched_setscheduler(pid, SCHED_RR, &params) == 0);
254         break;
255     case (KSysGuard::Process::Fifo):
256         success = (sched_setscheduler(pid, SCHED_FIFO, &params) == 0);
257         break;
258 #ifdef SCHED_BATCH
259     case (KSysGuard::Process::Batch):
260         success = (sched_setscheduler(pid, SCHED_BATCH, &params) == 0);
261         break;
262 #endif
263     }
264     if (success) {
265         return Processes::NoError;
266     }
267     return Processes::Unknown;
268 }
269 
setIoNiceness(long pid,int priorityClass,int priority)270 Processes::Error ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority)
271 {
272     return Processes::NotSupported; // Not yet supported
273 }
274 
supportsIoNiceness()275 bool ProcessesLocal::supportsIoNiceness()
276 {
277     return false;
278 }
279 
totalPhysicalMemory()280 long long ProcessesLocal::totalPhysicalMemory()
281 {
282     size_t Total;
283     size_t len;
284     len = sizeof(Total);
285     sysctlbyname("hw.physmem", &Total, &len, NULL, 0);
286     return Total /= 1024;
287 }
288 
~ProcessesLocal()289 ProcessesLocal::~ProcessesLocal()
290 {
291     delete d;
292 }
293 
294 }
295