1 /*
2     SPDX-FileCopyrightText: 2007 Manolo Valdes <nolis71cu@gmail.com>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "process.h"
8 #include "processes_local_p.h"
9 
10 #include <KLocalizedString>
11 
12 #include <QSet>
13 
14 #include <sys/param.h>
15 #include <sys/resource.h>
16 #include <sys/sysctl.h>
17 #include <sys/types.h>
18 #include <sys/user.h>
19 #if defined(__DragonFly__)
20 #include <err.h>
21 #include <sys/resourcevar.h>
22 #endif
23 #include <signal.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 
27 namespace KSysGuard
28 {
29 class ProcessesLocal::Private
30 {
31 public:
Private()32     Private()
33     {
34         ;
35     }
~Private()36     ~Private()
37     {
38         ;
39     }
40     inline bool readProc(long pid, struct kinfo_proc *p);
41     inline void readProcStatus(struct kinfo_proc *p, Process *process);
42     inline void readProcStat(struct kinfo_proc *p, Process *process);
43     inline void readProcStatm(struct kinfo_proc *p, Process *process);
44     inline bool readProcCmdline(long pid, Process *process);
45 };
46 
readProc(long pid,struct kinfo_proc * p)47 bool ProcessesLocal::Private::readProc(long pid, struct kinfo_proc *p)
48 {
49     int mib[4];
50     size_t len;
51 
52     mib[0] = CTL_KERN;
53     mib[1] = KERN_PROC;
54     mib[2] = KERN_PROC_PID;
55     mib[3] = pid;
56 
57     len = sizeof(struct kinfo_proc);
58     if (sysctl(mib, 4, p, &len, NULL, 0) == -1 || !len)
59         return false;
60     return true;
61 }
62 
readProcStatus(struct kinfo_proc * p,Process * process)63 void ProcessesLocal::Private::readProcStatus(struct kinfo_proc *p, Process *process)
64 {
65     process->setUid(0);
66     process->setGid(0);
67     process->setTracerpid(-1);
68 
69 #if defined(__FreeBSD__) && __FreeBSD_version >= 500015
70     process->setUid(p->ki_uid);
71     process->setGid(p->ki_pgid);
72     process->setName(QString(p->ki_comm ? p->ki_comm : "????"));
73 #elif defined(__DragonFly__) && __DragonFly_version >= 190000
74     process->setUid(p->kp_uid);
75     process->setGid(p->kp_pgid);
76     process->setName(QString(p->kp_comm ? p->kp_comm : "????"));
77 #else
78     process->setUid(p->kp_eproc.e_ucred.cr_uid);
79     process->setGid(p->kp_eproc.e_pgid);
80 #endif
81 }
82 
readProcStat(struct kinfo_proc * p,Process * ps)83 void ProcessesLocal::Private::readProcStat(struct kinfo_proc *p, Process *ps)
84 {
85     int status;
86     struct rusage pru;
87 #if defined(__FreeBSD__) && __FreeBSD_version >= 500015
88     ps->setUserTime(p->ki_runtime / 10000);
89     ps->setNiceLevel(p->ki_nice);
90     ps->setVmSize(p->ki_size);
91     ps->setVmRSS(p->ki_rssize * getpagesize());
92     status = p->ki_stat;
93 #elif defined(__DragonFly__) && __DragonFly_version >= 190000
94     if (!getrusage(p->kp_pid, &pru)) {
95         errx(1, "failed to get rusage info");
96     }
97     ps->setUserTime(pru.ru_utime.tv_usec / 1000); /*p_runtime / 1000*/
98     ps->setNiceLevel(p->kp_nice);
99     ps->setVmSize(p->kp_vm_map_size);
100     ps->setVmRSS(p->kp_vm_rssize * getpagesize());
101     status = p->kp_stat;
102 #else
103     ps->setUserTime(p->kp_proc.p_rtime.tv_sec * 100 + p->kp_proc.p_rtime.tv_usec / 100);
104     ps->setNiceLevel(p->kp_proc.p_nice);
105     ps->setVmSize(p->kp_eproc.e_vm.vm_map.size);
106     ps->setVmRSS(p->kp_eproc.e_vm.vm_rssize * getpagesize());
107     status = p->kp_proc.p_stat;
108 #endif
109     ps->setSysTime(0);
110 
111     // "idle","run","sleep","stop","zombie"
112     switch (status) {
113     case '0':
114         ps->setStatus(Process::DiskSleep);
115         break;
116     case '1':
117         ps->setStatus(Process::Running);
118         break;
119     case '2':
120         ps->setStatus(Process::Sleeping);
121         break;
122     case '3':
123         ps->setStatus(Process::Stopped);
124         break;
125     case '4':
126         ps->setStatus(Process::Zombie);
127         break;
128     default:
129         ps->setStatus(Process::OtherStatus);
130         break;
131     }
132 }
133 
readProcStatm(struct kinfo_proc * p,Process * process)134 void ProcessesLocal::Private::readProcStatm(struct kinfo_proc *p, Process *process)
135 {
136     // TODO
137 
138     //     unsigned long shared;
139     //     process->setVmURSS(process->vmRSS - (shared * sysconf(_SC_PAGESIZE) / 1024));
140 }
141 
readProcCmdline(long pid,Process * process)142 bool ProcessesLocal::Private::readProcCmdline(long pid, Process *process)
143 {
144     int mib[4];
145     struct kinfo_proc p;
146     size_t buflen = 256;
147     char buf[256];
148 
149     mib[0] = CTL_KERN;
150     mib[1] = KERN_PROC;
151     mib[2] = KERN_PROC_ARGS;
152     mib[3] = pid;
153 
154     if (sysctl(mib, 4, buf, &buflen, NULL, 0) == -1 || !buflen)
155         return false;
156     QString command = QString(buf);
157 
158     // cmdline separates parameters with the NULL character
159     command.replace('\0', ' ');
160     process->setCommand(command.trimmed());
161 
162     return true;
163 }
164 
ProcessesLocal()165 ProcessesLocal::ProcessesLocal()
166     : d(new Private())
167 {
168 }
169 
getParentPid(long pid)170 long ProcessesLocal::getParentPid(long pid)
171 {
172     Q_ASSERT(pid != 0);
173     long long ppid = -1;
174     struct kinfo_proc p;
175     if (d->readProc(pid, &p)) {
176 #if defined(__FreeBSD__) && __FreeBSD_version >= 500015
177         ppid = p.ki_ppid;
178 #elif defined(__DragonFly__) && __DragonFly_version >= 190000
179         ppid = p.kp_ppid;
180 #else
181         ppid = p.kp_eproc.e_ppid;
182 #endif
183     }
184     return ppid;
185 }
186 
updateProcessInfo(long pid,Process * process)187 bool ProcessesLocal::updateProcessInfo(long pid, Process *process)
188 {
189     struct kinfo_proc p;
190     if (!d->readProc(pid, &p))
191         return false;
192     d->readProcStat(&p, process);
193     d->readProcStatus(&p, process);
194     d->readProcStatm(&p, process);
195     if (!d->readProcCmdline(pid, process))
196         return false;
197 
198     return true;
199 }
200 
getAllPids()201 QSet<long> ProcessesLocal::getAllPids()
202 {
203     QSet<long> pids;
204     int mib[3];
205     size_t len;
206     size_t num;
207     struct kinfo_proc *p;
208 
209     mib[0] = CTL_KERN;
210     mib[1] = KERN_PROC;
211     mib[2] = KERN_PROC_ALL;
212     sysctl(mib, 3, NULL, &len, NULL, 0);
213     p = (kinfo_proc *)malloc(len);
214     sysctl(mib, 3, p, &len, NULL, 0);
215 
216     for (num = 0; num < len / sizeof(struct kinfo_proc); num++)
217 #if defined(__FreeBSD__) && __FreeBSD_version >= 500015
218         pids.insert(p[num].ki_pid);
219 #elif defined(__DragonFly__) && __DragonFly_version >= 190000
220         pids.insert(p[num].kp_pid);
221 #else
222         pids.insert(p[num].kp_proc.p_pid);
223 #endif
224     free(p);
225     return pids;
226 }
227 
sendSignal(long pid,int sig)228 Processes::Error ProcessesLocal::sendSignal(long pid, int sig)
229 {
230     if (kill((pid_t)pid, sig)) {
231         // Kill failed
232         return Processes::Unknown;
233     }
234     return Processes::NoError;
235 }
236 
setNiceness(long pid,int priority)237 Processes::Error ProcessesLocal::setNiceness(long pid, int priority)
238 {
239     if (setpriority(PRIO_PROCESS, pid, priority)) {
240         // set niceness failed
241         return Processes::Unknown;
242     }
243     return Processes::NoError;
244 }
245 
setScheduler(long pid,int priorityClass,int priority)246 Processes::Error ProcessesLocal::setScheduler(long pid, int priorityClass, int priority)
247 {
248     if (priorityClass == KSysGuard::Process::Other || priorityClass == KSysGuard::Process::Batch)
249         priority = 0;
250     if (pid <= 0)
251         return Processes::InvalidPid; // check the parameters
252     return Processes::NotSupported;
253 }
254 
setIoNiceness(long pid,int priorityClass,int priority)255 Processes::Error ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority)
256 {
257     return Processes::NotSupported; // Not yet supported
258 }
259 
supportsIoNiceness()260 bool ProcessesLocal::supportsIoNiceness()
261 {
262     return false;
263 }
264 
totalPhysicalMemory()265 long long ProcessesLocal::totalPhysicalMemory()
266 {
267     static int physmem_mib[] = {CTL_HW, HW_PHYSMEM};
268     /* get the page size with "getpagesize" and calculate pageshift from
269      * it */
270     int pagesize = ::getpagesize();
271     int pageshift = 0;
272     while (pagesize > 1) {
273         pageshift++;
274         pagesize >>= 1;
275     }
276     size_t Total = 0;
277     size_t size = sizeof(Total);
278     sysctl(physmem_mib, 2, &Total, &size, NULL, 0);
279     return Total /= 1024;
280 }
281 
numberProcessorCores()282 long int KSysGuard::ProcessesLocal::numberProcessorCores()
283 {
284     int mib[2];
285     int ncpu;
286     size_t len;
287 
288     mib[0] = CTL_HW;
289     mib[1] = HW_NCPU;
290     len = sizeof(ncpu);
291 
292     if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1 || !len)
293         return 1;
294     return len;
295 }
~ProcessesLocal()296 ProcessesLocal::~ProcessesLocal()
297 {
298     delete d;
299 }
300 
301 }
302