1 /*
2 * Copyright (c) 2010 Mike Massonnet <mmassonnet@xfce.org>
3 * Copyright (c) 2006 Oliver Lehmann <oliver@FreeBSD.org>
4 * Copyright (c) 2018 Rozhuk Ivan <rozhuk.im@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 #include <sys/param.h>
13 #include <kvm.h>
14 #include <sys/types.h>
15 #include <sys/sysctl.h>
16 #include <sys/user.h>
17 #include <sys/proc.h>
18 #include <fcntl.h>
19 #include <paths.h>
20 #include <unistd.h>
21 #include <string.h>
22 #if defined(__FreeBSD_version) && __FreeBSD_version >= 900044 || defined(__DragonFly_)
23 #include <sys/vmmeter.h>
24 #endif
25
26 #include <glib.h>
27
28 #include "task-manager.h"
29
30 #ifndef __DragonFly__
31 static const gchar ki_stat2state[] = {
32 ' ', /* - */
33 'R', /* SIDL */
34 'R', /* SRUN */
35 'S', /* SSLEEP */
36 'T', /* SSTOP */
37 'Z', /* SZOMB */
38 'W', /* SWAIT */
39 'L' /* SLOCK */
40 };
41 #endif
42
43
44 static guint64
get_mem_by_bytes(const gchar * name)45 get_mem_by_bytes (const gchar *name)
46 {
47 guint64 buf = 0;
48 gsize len = sizeof (buf);
49
50 if (sysctlbyname (name, &buf, &len, NULL, 0) < 0)
51 return 0;
52
53 return buf;
54 }
55
56 static guint64
get_mem_by_pages(const gchar * name)57 get_mem_by_pages (const gchar *name)
58 {
59 guint64 res;
60
61 res = get_mem_by_bytes (name);
62 if (res > 0)
63 res *= (guint64)getpagesize ();
64
65 return res;
66 }
67
68 gboolean
get_memory_usage(guint64 * memory_total,guint64 * memory_available,guint64 * memory_free,guint64 * memory_cache,guint64 * memory_buffers,guint64 * swap_total,guint64 * swap_free)69 get_memory_usage (guint64 *memory_total, guint64 *memory_available, guint64 *memory_free, guint64 *memory_cache, guint64 *memory_buffers, guint64 *swap_total, guint64 *swap_free)
70 {
71 /* Get memory usage */
72 {
73 *memory_total = get_mem_by_bytes ("hw.physmem");;
74 *memory_free = get_mem_by_pages ("vm.stats.vm.v_free_count");
75 *memory_cache = get_mem_by_pages ("vm.stats.vm.v_inactive_count");
76 *memory_buffers = get_mem_by_bytes ("vfs.bufspace");
77 *memory_available = *memory_free + *memory_cache + *memory_buffers;
78 }
79
80 /* Get swap usage */
81 {
82 kvm_t *kd;
83 struct kvm_swap kswap;
84
85 if ((kd = kvm_openfiles (_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL)) == NULL)
86 return FALSE;
87
88 kvm_getswapinfo (kd, &kswap, 1, 0);
89 *swap_total = (guint64)kswap.ksw_total * (guint64)getpagesize ();
90 *swap_free = ((guint64)(kswap.ksw_total - kswap.ksw_used)) * (guint64)getpagesize ();
91
92 kvm_close (kd);
93 }
94
95 return TRUE;
96 }
97
98 gboolean
get_cpu_usage(gushort * cpu_count,gfloat * cpu_user,gfloat * cpu_system)99 get_cpu_usage (gushort *cpu_count, gfloat *cpu_user, gfloat *cpu_system)
100 {
101 /* Get CPU count */
102 {
103 size_t len = sizeof (*cpu_count);
104 sysctlbyname ("hw.ncpu", cpu_count, &len, NULL, 0);
105 }
106
107 /* Get CPU usage */
108 {
109 static gulong cp_user = 0, cp_system = 0, cp_total = 0;
110 static gulong cp_user_old = 0, cp_system_old = 0, cp_total_old = 0;
111
112 gulong cpu_state[CPUSTATES] = { 0 };
113 size_t len = sizeof (cpu_state);
114 sysctlbyname ("kern.cp_time", &cpu_state, &len, NULL, 0);
115
116 cp_user_old = cp_user;
117 cp_system_old = cp_system;
118 cp_total_old = cp_total;
119 cp_user = cpu_state[CP_USER] + cpu_state[CP_NICE];
120 cp_system = cpu_state[CP_SYS] + cpu_state[CP_INTR];
121 cp_total = cpu_state[CP_IDLE] + cp_user + cp_system;
122
123 *cpu_user = *cpu_system = 0.0f;
124 if (cp_total > cp_total_old)
125 {
126 *cpu_user = (((cp_user - cp_user_old) * 100.0f) / (float)(cp_total - cp_total_old));
127 *cpu_system = (((cp_system - cp_system_old) * 100.0f) / (float)(cp_total - cp_total_old));
128 }
129 }
130
131 return TRUE;
132 }
133
134 static gboolean
get_task_details(struct kinfo_proc * kp,Task * task)135 get_task_details (struct kinfo_proc *kp, Task *task)
136 {
137 char buf[1024], *p;
138 size_t bufsz;
139 int i, oid[4];
140
141 memset(task, 0, sizeof(Task));
142 #ifdef __DragonFly__
143 task->pid = kp->kp_pid;
144 task->ppid = kp->kp_ppid;
145 task->cpu_user = 100.0f * ((float)kp->kp_lwp.kl_pctcpu / FSCALE);
146 #else
147 task->pid = kp->ki_pid;
148 task->ppid = kp->ki_ppid;
149 task->cpu_user = 100.0f * ((float)kp->ki_pctcpu / FSCALE);
150 #endif
151 task->cpu_system = 0.0f;
152 #ifdef __DragonFly__
153 task->vsz = kp->kp_vm_map_size;
154 task->rss = ((guint64)kp->kp_vm_rssize * (guint64)getpagesize ());
155 task->uid = kp->kp_uid;
156 task->prio = (gshort)kp->kp_nice;
157 g_strlcpy (task->name, kp->kp_comm, sizeof(task->name));
158 #else
159 task->vsz = kp->ki_size;
160 task->rss = ((guint64)kp->ki_rssize * (guint64)getpagesize ());
161 task->uid = kp->ki_uid;
162 task->prio = (gshort)kp->ki_nice;
163 g_strlcpy (task->name, kp->ki_comm, sizeof(task->name));
164 #endif
165
166 oid[0] = CTL_KERN;
167 oid[1] = KERN_PROC;
168 oid[2] = KERN_PROC_ARGS;
169 #ifdef __DragonFly__
170 oid[3] = kp->kp_pid;
171 #else
172 oid[3] = kp->ki_pid;
173 #endif
174 bufsz = sizeof(buf);
175 memset(buf, 0, sizeof(buf));
176 if (sysctl(oid, 4, buf, &bufsz, 0, 0) == -1) {
177 /*
178 * If the supplied buf is too short to hold the requested
179 * value the sysctl returns with ENOMEM. The buf is filled
180 * with the truncated value and the returned bufsz is equal
181 * to the requested len.
182 */
183 if (errno != ENOMEM || bufsz != sizeof(buf)) {
184 bufsz = 0;
185 } else {
186 buf[(bufsz - 1)] = 0;
187 }
188 }
189
190 if (0 != bufsz) {
191 p = buf;
192 do {
193 g_strlcat (task->cmdline, p, sizeof(task->cmdline));
194 g_strlcat (task->cmdline, " ", sizeof(task->cmdline));
195 p += (strlen(p) + 1);
196 } while (p < buf + bufsz);
197 }
198 else
199 {
200 #ifdef __DragonFly__
201 g_strlcpy (task->cmdline, kp->kp_comm, sizeof(task->cmdline));
202 #else
203 g_strlcpy (task->cmdline, kp->ki_comm, sizeof(task->cmdline));
204 #endif
205 }
206
207 i = 0;
208 #ifdef __DragonFly__
209 switch (kp->kp_stat)
210 {
211 case SACTIVE:
212 switch (kp->kp_lwp.kl_stat) {
213 case LSRUN:
214 task->state[i] = 'R';
215 break;
216 case LSSLEEP:
217 task->state[i] = 'S';
218 break;
219 case LSSTOP:
220 task->state[i] = 'T';
221 break;
222 default:
223 task->state[i] = '?';
224 break;
225 }
226 case SZOMB:
227 task->state[i] = 'Z';
228 break;
229 case SIDL:
230 task->state[i] = 'R';
231 default:
232 task->state[i] = '?';
233 }
234 #else
235 switch (kp->ki_stat)
236 {
237 case SIDL:
238 case SRUN:
239 case SSTOP:
240 case SZOMB:
241 case SWAIT:
242 case SLOCK:
243 task->state[i] = ki_stat2state[(size_t)kp->ki_stat];
244 break;
245
246 case SSLEEP:
247 if (kp->ki_tdflags & TDF_SINTR)
248 task->state[i] = kp->ki_slptime >= MAXSLP ? 'I' : 'S';
249 else
250 task->state[i] = 'D';
251 break;
252
253 default:
254 task->state[i] = '?';
255 }
256 #endif
257 i++;
258 #ifndef __DragonFly__
259 if (!(kp->ki_sflag & PS_INMEM))
260 task->state[i++] = 'W';
261 #endif
262 #ifdef __DragonFly__
263 if (kp->kp_nice < NZERO)
264 task->state[i++] = '<';
265 else if (kp->kp_nice > NZERO)
266 task->state[i++] = 'N';
267 #else
268 if (kp->ki_nice < NZERO)
269 task->state[i++] = '<';
270 else if (kp->ki_nice > NZERO)
271 task->state[i++] = 'N';
272 #endif
273 #ifdef __DragonFly__
274 if (kp->kp_flags & P_TRACED)
275 task->state[i++] = 'X';
276 if (kp->kp_flags & P_WEXIT && kp->kp_stat != SZOMB)
277 task->state[i++] = 'E';
278 if (kp->kp_flags & P_PPWAIT)
279 task->state[i++] = 'V';
280 if ((kp->kp_flags & P_SYSTEM) || kp->kp_lock > 0)
281 task->state[i++] = 'L';
282 #else
283 if (kp->ki_flag & P_TRACED)
284 task->state[i++] = 'X';
285 if (kp->ki_flag & P_WEXIT && kp->ki_stat != SZOMB)
286 task->state[i++] = 'E';
287 if (kp->ki_flag & P_PPWAIT)
288 task->state[i++] = 'V';
289 if ((kp->ki_flag & P_SYSTEM) || kp->ki_lock > 0)
290 task->state[i++] = 'L';
291 #endif
292 #ifdef __DragonFly__
293 if ((kp->kp_flags & P_CONTROLT) && kp->kp_pgid == kp->kp_tpgid)
294 task->state[i++] = '+';
295 if (kp->kp_flags & P_JAILED)
296 task->state[i++] = 'J';
297 #else
298 if (kp->ki_kiflag & KI_SLEADER)
299 task->state[i++] = 's';
300 if ((kp->ki_flag & P_CONTROLT) && kp->ki_pgid == kp->ki_tpgid)
301 task->state[i++] = '+';
302 if (kp->ki_flag & P_JAILED)
303 task->state[i++] = 'J';
304 #endif
305
306 return TRUE;
307 }
308
309 gboolean
get_task_list(GArray * task_list)310 get_task_list (GArray *task_list)
311 {
312 kvm_t *kd;
313 struct kinfo_proc *kp;
314 int cnt = 0, i;
315 Task task;
316
317 if ((kd = kvm_openfiles (_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL)) == NULL)
318 return FALSE;
319
320 #ifdef __DragonFly__
321 if ((kp = kvm_getprocs (kd, KERN_PROC_ALL, 0, &cnt)) == NULL)
322 #else
323 if ((kp = kvm_getprocs (kd, KERN_PROC_PROC, 0, &cnt)) == NULL)
324 #endif
325 {
326 kvm_close (kd);
327 return FALSE;
328 }
329
330 for (i = 0; i < cnt; kp++, i++)
331 {
332 get_task_details (kp, &task);
333 g_array_append_val (task_list, task);
334 }
335
336 kvm_close (kd);
337
338 g_array_sort (task_list, task_pid_compare_fn);
339
340 return TRUE;
341 }
342
343 gboolean
pid_is_sleeping(GPid pid)344 pid_is_sleeping (GPid pid)
345 {
346 kvm_t *kd;
347 struct kinfo_proc *kp;
348 gint cnt;
349 gboolean ret;
350
351 if ((kd = kvm_openfiles (_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL)) == NULL)
352 return FALSE;
353
354 if ((kp = kvm_getprocs (kd, KERN_PROC_PID, pid, &cnt)) == NULL)
355 {
356 kvm_close (kd);
357 return FALSE;
358 }
359
360 #ifdef __DragonFly__
361 ret = (kp->kp_stat == SSTOP);
362 #else
363 ret = (kp->ki_stat == SSTOP);
364 #endif
365 kvm_close (kd);
366
367 return ret;
368 }
369