1 /*
2     SPDX-FileCopyrightText: 2007 Adriaan de Groot <groot@kde.org>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 /* Stop <sys/procfs.h> from crapping out on 32-bit architectures. */
8 
9 #if !defined(_LP64) && _FILE_OFFSET_BITS == 64
10 #undef _FILE_OFFSET_BITS
11 #define _FILE_OFFSET_BITS 32
12 #endif
13 
14 #include "process.h"
15 #include "processes_local_p.h"
16 
17 #include <KLocalizedString>
18 
19 #include <QSet>
20 
21 #include <dirent.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <procfs.h>
25 #include <pwd.h>
26 #include <sched.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/param.h>
32 #include <sys/proc.h>
33 #include <sys/resource.h>
34 #include <sys/types.h>
35 #include <sys/user.h>
36 #include <unistd.h>
37 
38 #define PROCESS_BUFFER_SIZE 512
39 #define PROCDIR "/proc"
40 
41 namespace KSysGuard
42 {
43 class ProcessesLocal::Private
44 {
45 public:
Private()46     Private()
47     {
48         mProcDir = opendir(PROCDIR);
49     };
~Private()50     ~Private(){};
51     char mBuf[PROCESS_BUFFER_SIZE + 1]; // used as a buffer to read data into
52     DIR *mProcDir;
53 };
54 
ProcessesLocal()55 ProcessesLocal::ProcessesLocal()
56     : d(new Private())
57 {
58 }
59 
getParentPid(long pid)60 long ProcessesLocal::getParentPid(long pid)
61 {
62     long long ppid = -1;
63     int fd;
64     psinfo_t psinfo;
65 
66     snprintf(d->mBuf, PROCESS_BUFFER_SIZE - 1, "%s/%ld/psinfo", PROCDIR, pid);
67     if ((fd = open(d->mBuf, O_RDONLY)) < 0) {
68         return -1; /* process has terminated in the meantime */
69     }
70 
71     if (read(fd, &psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)) {
72         close(fd);
73         return -1;
74     }
75     close(fd);
76     ppid = psinfo.pr_ppid;
77 
78     return ppid;
79 }
80 
updateProcessInfo(long pid,Process * process)81 bool ProcessesLocal::updateProcessInfo(long pid, Process *process)
82 {
83     int fd, pfd;
84     psinfo_t psinfo;
85     prusage_t prusage;
86 
87     snprintf(d->mBuf, PROCESS_BUFFER_SIZE - 1, "%s/%ld/psinfo", PROCDIR, pid);
88     if ((fd = open(d->mBuf, O_RDONLY)) < 0) {
89         return false; /* process has terminated in the meantime */
90     }
91 
92     snprintf(d->mBuf, PROCESS_BUFFER_SIZE - 1, "%s/%ld/usage", PROCDIR, pid);
93     if ((pfd = open(d->mBuf, O_RDONLY)) < 0) {
94         close(fd);
95         return false; /* process has terminated in the meantime */
96     }
97 
98     process->uid = 0;
99     process->gid = 0;
100     process->tracerpid = -1;
101     process->pid() = pid;
102 
103     if (read(fd, &psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)) {
104         close(fd);
105         return false;
106     }
107     close(fd);
108 
109     if (read(pfd, &prusage, sizeof(prusage_t)) != sizeof(prusage_t)) {
110         close(pfd);
111         return false;
112     }
113     close(pfd);
114 
115     process->setUid(psinfo.pr_uid);
116     process->setEuid(psinfo.pr_euid);
117     process->setGid(psinfo.pr_gid);
118     process->setEgid(psinfo.pr_egid);
119 
120     switch ((int)psinfo.pr_lwp.pr_state) {
121     case SIDL:
122     case SWAIT:
123     case SSLEEP:
124         process->setStatus(Process::Sleeping);
125         break;
126     case SONPROC:
127     case SRUN:
128         process->setStatus(Process::Running);
129         break;
130     case SZOMB:
131         process->setStatus(Process::Zombie);
132         break;
133     case SSTOP:
134         process->setStatus(Process::Stopped);
135         break;
136     default:
137         process->setStatus(Process::OtherStatus);
138         break;
139     }
140 
141     process->setVmRSS(psinfo.pr_rssize);
142     process->setVmSize(psinfo.pr_size);
143     process->setVmURSS(-1);
144 
145     if (process->command.isNull()) {
146         QString name(psinfo.pr_fname);
147 
148         name = name.trimmed();
149         if (!name.isEmpty()) {
150             name.remove(QRegExp("^[^ ]*/"));
151         }
152         process->setName(name);
153         name = psinfo.pr_fname;
154         name.append(psinfo.pr_psargs);
155         process->setCommand(name);
156     }
157 
158     // Approximations, not quite accurate. Needs more changes in ksysguard to map
159     // RR and FIFO to current Solaris classes.
160     if (strcmp(psinfo.pr_lwp.pr_clname, "TS") == 0 || strcmp(psinfo.pr_lwp.pr_clname, "SYS") == 0 || strcmp(psinfo.pr_lwp.pr_clname, "FSS") == 0) {
161         process->setscheduler(KSysGuard::Process::Other);
162 
163     } else if (strcmp(psinfo.pr_lwp.pr_clname, "FX") == 0 || strcmp(psinfo.pr_lwp.pr_clname, "RT") == 0) {
164         process->setscheduler(KSysGuard::Process::RoundRobin);
165 
166     } else if (strcmp(psinfo.pr_lwp.pr_clname, "IA") == 0) {
167         process->setscheduler(KSysGuard::Process::Interactive);
168     }
169     process->setNiceLevel(psinfo.pr_lwp.pr_pri);
170     process->setUserTime(prusage.pr_utime.tv_sec * 100 + prusage.pr_utime.tv_nsec / 10000000.0);
171     process->setSysTime(prusage.pr_stime.tv_sec * 100 + prusage.pr_stime.tv_nsec / 10000000.0);
172     return false;
173 }
174 
getAllPids()175 QSet<long> ProcessesLocal::getAllPids()
176 {
177     QSet<long> pids;
178     long pid;
179 
180     if (d->mProcDir == NULL)
181         return pids; // There's not much we can do without /proc
182     struct dirent *entry;
183     rewinddir(d->mProcDir);
184     while ((entry = readdir(d->mProcDir)))
185         if (entry->d_name[0] >= '0' && entry->d_name[0] <= '9') {
186             pid = atol(entry->d_name);
187             // Skip all processes with parent id = 0 except init
188             if (pid == 1 || getParentPid(pid) > 0) {
189                 pids.insert(pid);
190             }
191         }
192     return pids;
193 }
194 
sendSignal(long pid,int sig)195 Processes::Error ProcessesLocal::sendSignal(long pid, int sig)
196 {
197     if (kill((pid_t)pid, sig)) {
198         // Kill failed
199         return Processes::Unknown;
200     }
201     return Processes::NoError;
202 }
203 
204 /*
205  *
206  */
setNiceness(long pid,int priority)207 Processes::Error ProcessesLocal::setNiceness(long pid, int priority)
208 {
209     return Processes::NotSupported;
210 }
211 
setScheduler(long pid,int priorityClass,int priority)212 Processes::Error ProcessesLocal::setScheduler(long pid, int priorityClass, int priority)
213 {
214     return Processes::NotSupported;
215 }
216 
setIoNiceness(long pid,int priorityClass,int priority)217 Processes::Error ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority)
218 {
219     return Processes::NotSupported; // Not yet supported
220 }
221 
supportsIoNiceness()222 bool ProcessesLocal::supportsIoNiceness()
223 {
224     return false;
225 }
226 
totalPhysicalMemory()227 long long ProcessesLocal::totalPhysicalMemory()
228 {
229     long long memory = ((long long)sysconf(_SC_PHYS_PAGES)) * (sysconf(_SC_PAGESIZE) / 1024);
230     if (memory > 0)
231         return memory;
232     return 0;
233 }
234 
~ProcessesLocal()235 ProcessesLocal::~ProcessesLocal()
236 {
237     delete d;
238 }
239 
240 }
241