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, ¶ms) == 0);
251 break;
252 case (KSysGuard::Process::RoundRobin):
253 success = (sched_setscheduler(pid, SCHED_RR, ¶ms) == 0);
254 break;
255 case (KSysGuard::Process::Fifo):
256 success = (sched_setscheduler(pid, SCHED_FIFO, ¶ms) == 0);
257 break;
258 #ifdef SCHED_BATCH
259 case (KSysGuard::Process::Batch):
260 success = (sched_setscheduler(pid, SCHED_BATCH, ¶ms) == 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