1 /*
2  * Copyright (c) 2008-2014 Landry Breuil <landry@xfce.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #include <stdlib.h>
20 #include <err.h>
21 #include <sys/types.h>
22 /* for sysctl() */
23 #include <sys/param.h>
24 #include <sys/sched.h>
25 #include <sys/sysctl.h>
26 /* for swapctl() */
27 #include <sys/swap.h>
28 /* for strlcpy() */
29 #include <string.h>
30 /* for getpagesize() */
31 #include <unistd.h>
32 /* for P_ZOMBIE & SSLEEP */
33 #include <sys/proc.h>
34 /* for struct vmtotal */
35 #include <sys/vmmeter.h>
36 #include "task-manager.h"
37 
38 #include <errno.h>
39 extern int errno;
40 
41 char	*state_abbrev[] = {
42 	"", "start", "run", "sleep", "stop", "zomb", "dead", "onproc"
43 };
44 
get_task_list(GArray * task_list)45 gboolean get_task_list (GArray *task_list)
46 {
47 	int mib[6];
48 	size_t size;
49 #ifdef __OpenBSD__
50 	struct kinfo_proc *kp;
51 #else
52 	struct kinfo_proc2 *kp;
53 #endif
54 	Task t;
55 	char **args;
56 	gchar* buf;
57 	int nproc, i;
58 
59 	mib[0] = CTL_KERN;
60 #ifdef __OpenBSD__
61 	mib[1] = KERN_PROC;
62 #else
63 	mib[1] = KERN_PROC2;
64 #endif
65 	mib[2] = KERN_PROC_ALL;
66 	mib[3] = 0;
67 #ifdef __OpenBSD__
68 	mib[4] = sizeof(struct kinfo_proc);
69 #else
70 	mib[4] = sizeof(struct kinfo_proc2);
71 #endif
72 	mib[5] = 0;
73 	if (sysctl(mib, 6, NULL, &size, NULL, 0) < 0)
74 #ifdef __OpenBSD__
75 		errx(1, "could not get kern.proc size");
76 #else
77 		errx(1, "could not get kern.proc2 size");
78 #endif
79 	size = 5 * size / 4;		/* extra slop */
80 	if ((kp = malloc(size)) == NULL)
81 		errx(1,"failed to allocate memory for proc structures");
82 #ifdef __OpenBSD__
83 	mib[5] = (int)(size / sizeof(struct kinfo_proc));
84 #else
85 	mib[5] = (int)(size / sizeof(struct kinfo_proc2));
86 #endif
87 	if (sysctl(mib, 6, kp, &size, NULL, 0) < 0)
88 #ifdef __OpenBSD__
89 		errx(1, "could not read kern.proc");
90 	nproc = (int)(size / sizeof(struct kinfo_proc));
91 #else
92 		errx(1, "could not read kern.proc2");
93 	nproc = (int)(size / sizeof(struct kinfo_proc2));
94 #endif
95 	for (i=0 ; i < nproc ; i++)
96 	{
97 #ifdef __OpenBSD__
98 		struct kinfo_proc p = kp[i];
99 #else
100 		struct kinfo_proc2 p = kp[i];
101 #endif
102 		memset(&t, 0, sizeof(t));
103 		t.pid = p.p_pid;
104 		t.ppid = p.p_ppid;
105 		t.uid = p.p_uid;
106 		t.prio = p.p_priority - PZERO;
107 		t.vsz = p.p_vm_dsize + p.p_vm_ssize + p.p_vm_tsize;
108 		t.vsz *= getpagesize();
109 		t.rss = p.p_vm_rssize * getpagesize();
110 		g_snprintf(t.state, sizeof t.state, "%s", state_abbrev[p.p_stat]);
111 		g_strlcpy(t.name, p.p_comm, strlen(p.p_comm) + 1);
112 		/* shamelessly stolen from top/machine.c */
113 		if (!P_ZOMBIE(&p)) {
114 			size = 1024;
115 			if ((args = malloc(size)) == NULL)
116 				errx(1,"failed to allocate memory for argv structures at %zu", size);
117 			memset(args, 0, size);
118 			for (;; size *= 2) {
119 				if ((args = realloc(args, size)) == NULL)
120 					errx(1,"failed to allocate memory (size=%zu) for argv structures of pid %d", size, t.pid);
121 				memset(args, 0, size);
122 				mib[0] = CTL_KERN;
123 				mib[1] = KERN_PROC_ARGS;
124 				mib[2] = t.pid;
125 				mib[3] = KERN_PROC_ARGV;
126 				if (sysctl(mib, 4, args, &size, NULL, 0) == 0)
127 					break;
128 				if (errno != ENOMEM) { /* ESRCH: process disappeared */
129 					 /* printf ("process with pid %d disappeared, errno=%d\n", t.pid, errno); */
130 					args[0] ='\0';
131 					args[1] = NULL;
132 					break;
133 				}
134 			}
135 			buf = g_strjoinv(" ", args);
136 			g_assert(g_utf8_validate(buf, -1, NULL));
137 			g_strlcpy(t.cmdline, buf, sizeof t.cmdline);
138 			g_free(buf);
139 			free(args);
140 		}
141 
142 		t.cpu_user = (100.0f * ((gfloat)p.p_pctcpu / FSCALE));
143 		t.cpu_system = 0.0f; /* TODO ? */
144 		g_array_append_val(task_list, t);
145 	}
146 	free(kp);
147 
148 	g_array_sort (task_list, task_pid_compare_fn);
149 
150 	return TRUE;
151 }
152 
153 gboolean
pid_is_sleeping(GPid pid)154 pid_is_sleeping (GPid pid)
155 {
156 	int mib[6];
157 #ifdef __OpenBSD__
158 	struct kinfo_proc kp;
159 	size_t size = sizeof(struct kinfo_proc);
160 #else
161 	struct kinfo_proc2 kp;
162 	size_t size = sizeof(struct kinfo_proc2);
163 #endif
164 
165 	mib[0] = CTL_KERN;
166 #ifdef __OpenBSD__
167 	mib[1] = KERN_PROC;
168 #else
169 	mib[1] = KERN_PROC2;
170 #endif
171 	mib[2] = KERN_PROC_PID;
172 	mib[3] = pid;
173 #ifdef __OpenBSD__
174 	mib[4] = sizeof(struct kinfo_proc);
175 #else
176 	mib[4] = sizeof(struct kinfo_proc2);
177 #endif
178 	mib[5] = 1;
179 	if (sysctl(mib, 6, &kp, &size, NULL, 0) < 0)
180 #ifdef __OpenBSD__
181 		errx(1, "could not read kern.proc for pid %d", pid);
182 #else
183 		errx(1, "could not read kern.proc2 for pid %d", pid);
184 #endif
185 	return (kp.p_stat == SSTOP ? TRUE : FALSE);
186 }
187 
get_cpu_usage(gushort * cpu_count,gfloat * cpu_user,gfloat * cpu_system)188 gboolean get_cpu_usage (gushort *cpu_count, gfloat *cpu_user, gfloat *cpu_system)
189 {
190 	static gulong cur_user = 0, cur_system = 0, cur_total = 0;
191 	static gulong old_user = 0, old_system = 0, old_total = 0;
192 
193 	int mib[] = {CTL_KERN, KERN_CPTIME};
194  	glong cp_time[CPUSTATES];
195  	gsize size = sizeof( cp_time );
196 	if (sysctl(mib, 2, &cp_time, &size, NULL, 0) < 0)
197 		errx(1,"failed to get kern.cptime");
198 
199 	old_user = cur_user;
200 	old_system = cur_system;
201 	old_total = cur_total;
202 
203 	cur_user = cp_time[CP_USER] + cp_time[CP_NICE];
204 	cur_system = cp_time[CP_SYS] + cp_time[CP_INTR];
205 	cur_total = cur_user + cur_system + cp_time[CP_IDLE];
206 
207 	*cpu_user = (old_total > 0) ? (((cur_user - old_user) * 100.0f) / (float)(cur_total - old_total)) : 0.0f;
208 	*cpu_system = (old_total > 0) ? (((cur_system - old_system) * 100.0f) / (float)(cur_total - old_total)) : 0.0f;
209 
210 	/* get #cpu */
211 	size = sizeof(&cpu_count);
212 	mib[0] = CTL_HW;
213 	mib[1] = HW_NCPU;
214 	if (sysctl(mib, 2, cpu_count, &size, NULL, 0) == -1)
215 		errx(1,"failed to get cpu count");
216 	return TRUE;
217 }
218 
219 /* vmtotal values in #pg */
220 #define pagetok(nb) ((nb) * (getpagesize()))
221 
222 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)223 get_memory_usage (guint64 *memory_total, guint64 *memory_available, guint64 *memory_free, guint64 *memory_cache, guint64 *memory_buffers, guint64 *swap_total, guint64 *swap_free)
224 {
225 #ifdef __OpenBSD__
226 	int mib[] = {CTL_VM, VM_UVMEXP};
227 	struct uvmexp  uvmexp;
228 #else
229 	int mib[] = {CTL_VM, VM_METER};
230 	struct vmtotal vmtotal;
231 #endif
232 	struct swapent *swdev;
233 	int nswap, i;
234 	size_t size;
235 #ifdef __OpenBSD__
236 	size = sizeof(uvmexp);
237 	if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0)
238 		errx(1,"failed to get vm.uvmexp");
239 	/* cheat : rm = tot used, add free to get total */
240 	*memory_free = pagetok((guint64)uvmexp.free);
241 	*memory_total = pagetok((guint64)uvmexp.npages);
242 	*memory_cache = 0;
243 	*memory_buffers = 0; /*pagetok(uvmexp.npages - uvmexp.free - uvmexp.active);*/
244 #else
245 	size = sizeof(vmtotal);
246 	if (sysctl(mib, 2, &vmtotal, &size, NULL, 0) < 0)
247 		errx(1,"failed to get vm.meter");
248 	/* cheat : rm = tot used, add free to get total */
249 	*memory_total = pagetok(vmtotal.t_rm + vmtotal.t_free);
250 	*memory_free = pagetok(vmtotal.t_free);
251 	*memory_cache = 0;
252 	*memory_buffers = pagetok(vmtotal.t_rm - vmtotal.t_arm);
253 #endif
254 	*memory_available = *memory_free + *memory_cache + *memory_buffers;
255 
256 	/* get swap stats */
257 	if ((nswap = swapctl(SWAP_NSWAP, 0, 0)) == 0)
258 		errx(1,"failed to get swap device count");
259 
260 	if ((swdev = calloc(nswap, sizeof(*swdev))) == NULL)
261 		errx(1,"failed to allocate memory for swdev structures");
262 
263 	if (swapctl(SWAP_STATS, swdev, nswap) == -1) {
264 		free(swdev);
265 		errx(1,"failed to get swap stats");
266 	}
267 
268 	/* Total things up */
269 	*swap_total = *swap_free = 0;
270 	for (i = 0; i < nswap; i++) {
271 		if (swdev[i].se_flags & SWF_ENABLE) {
272 			*swap_free += (swdev[i].se_nblks - swdev[i].se_inuse);
273 			*swap_total += swdev[i].se_nblks;
274 		}
275 	}
276 	*swap_total *= DEV_BSIZE;
277 	*swap_free *= DEV_BSIZE;
278 	free(swdev);
279 	return TRUE;
280 }
281 
282