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