ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/process_stats.c
Revision: 1.37
Committed: Sun Apr 4 21:34:48 2004 UTC (20 years, 1 month ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.36: +90 -31 lines
Log Message:
This has been a bit more complex than it probably should have been. We now
have the command line arguments on FreeBSD5, NetBSD, and probably OpenBSD
without using kvm. The sysctl interface provided the required information.

However, on FreeBSD 4 and Dragonfly BSD there was no matching sysctl
interface, so the kvm way remains. Fortunately neither of those two OS's
require elevated privileges to use that bit of kvm.

Finally in a decent position with no elevated privileges required for this
section of code, on any of the BSD's.

Still one bug to fix: there are 5 "null" processes on the end of the list.
This has been introduced at some point, because it wasn't there yesterday.

File Contents

# Content
1 /*
2 * i-scream central monitoring system
3 * http://www.i-scream.org
4 * Copyright (C) 2000-2004 i-scream
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 * 02111-1307 USA
20 *
21 * $Id: process_stats.c,v 1.36 2004/04/04 16:12:59 tdb Exp $
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "statgrab.h"
29 #if defined(SOLARIS) || defined(LINUX)
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <dirent.h>
34 #include <string.h>
35 #endif
36
37 #ifdef SOLARIS
38 #include <procfs.h>
39 #include <limits.h>
40 #define PROC_LOCATION "/proc"
41 #define MAX_FILE_LENGTH PATH_MAX
42 #endif
43 #ifdef LINUX
44 #include <limits.h>
45 #include <unistd.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #define PROC_LOCATION "/proc"
49 #define MAX_FILE_LENGTH PATH_MAX
50 #endif
51 #ifdef ALLBSD
52 #include <stdlib.h>
53 #include <sys/param.h>
54 #include <sys/sysctl.h>
55 #if defined(FREEBSD) || defined(DFBSD)
56 #include <sys/user.h>
57 #else
58 #include <sys/proc.h>
59 #endif
60 #include <string.h>
61 #include <paths.h>
62 #include <fcntl.h>
63 #include <limits.h>
64 #include <kvm.h>
65 #endif
66
67 int get_proc_snapshot(proc_state_t **ps){
68 proc_state_t *proc_state = NULL;
69 proc_state_t *proc_state_ptr;
70 int proc_state_size = 0;
71 #ifdef ALLBSD
72 int mib[4];
73 size_t size;
74 struct kinfo_proc *kp_stats;
75 int procs, i, alloc;
76 char *proctitle;
77 #if defined(FREEBSD5) || defined(NETBSD) || defined(OPENBSD)
78 long buflen;
79 char *p;
80 int argc;
81 int j = 0;
82 #else
83 static kvm_t *kvmd;
84 char **args;
85 #endif
86 #endif
87 #if defined(SOLARIS) || defined(LINUX)
88 DIR *proc_dir;
89 struct dirent *dir_entry;
90 char filename[MAX_FILE_LENGTH];
91 FILE *f;
92 #ifdef SOLARIS
93 psinfo_t process_info;
94 #endif
95 #ifdef LINUX
96 char s;
97 /* If someone has a executable of 4k filename length, they deserve to get it truncated :) */
98 char ps_name[4096];
99 char *ptr;
100 static char *psargs = NULL;
101 static int psarg_size = 0;
102 unsigned long stime, utime;
103 int x;
104 int fn;
105 int toread;
106 ssize_t size;
107 int t_read;
108 #endif
109
110 if((proc_dir=opendir(PROC_LOCATION))==NULL){
111 return -1;
112 }
113
114 while((dir_entry=readdir(proc_dir))!=NULL){
115 if(atoi(dir_entry->d_name) == 0) continue;
116
117 #ifdef SOLARIS
118 snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/psinfo", dir_entry->d_name);
119 #endif
120 #ifdef LINUX
121 snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/stat", dir_entry->d_name);
122 #endif
123 if((f=fopen(filename, "r"))==NULL){
124 /* Open failed.. Process since vanished, or the path was too long.
125 * Ah well, move onwards to the next one */
126 continue;
127 }
128 #ifdef SOLARIS
129 fread(&process_info, sizeof(psinfo_t), 1, f);
130 #endif
131
132 proc_state = realloc(proc_state, (1+proc_state_size)*sizeof(proc_state_t));
133 proc_state_ptr = proc_state+proc_state_size;
134 #ifdef SOLARIS
135 proc_state_ptr->pid = process_info.pr_pid;
136 proc_state_ptr->parent = process_info.pr_ppid;
137 proc_state_ptr->pgid = process_info.pr_pgid;
138 proc_state_ptr->uid = process_info.pr_uid;
139 proc_state_ptr->euid = process_info.pr_euid;
140 proc_state_ptr->gid = process_info.pr_gid;
141 proc_state_ptr->egid = process_info.pr_egid;
142 proc_state_ptr->proc_size = (process_info.pr_size) * 1024;
143 proc_state_ptr->proc_resident = (process_info.pr_rssize) * 1024;
144 proc_state_ptr->time_spent = process_info.pr_time.tv_sec;
145 proc_state_ptr->cpu_percent = (process_info.pr_pctcpu * 100.0) / 0x8000;
146 proc_state_ptr->process_name = strdup(process_info.pr_fname);
147 proc_state_ptr->proctitle = strdup(process_info.pr_psargs);
148
149 if(process_info.pr_lwp.pr_state==1) proc_state_ptr->state = SLEEPING;
150 if(process_info.pr_lwp.pr_state==2) proc_state_ptr->state = RUNNING;
151 if(process_info.pr_lwp.pr_state==3) proc_state_ptr->state = ZOMBIE;
152 if(process_info.pr_lwp.pr_state==4) proc_state_ptr->state = STOPPED;
153 if(process_info.pr_lwp.pr_state==6) proc_state_ptr->state = RUNNING;
154 #endif
155 #ifdef LINUX
156 x = fscanf(f, "%d %4096s %c %d %d %*d %*d %*d %*lu %*lu %*lu %*lu %*lu %lu %lu %*ld %*ld %*ld %d %*ld %*ld %*lu %llu %llu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*d %*d\n", &(proc_state_ptr->pid), ps_name, &s, &(proc_state_ptr->parent), &(proc_state_ptr->pgid), &utime, &stime, &(proc_state_ptr->nice), &(proc_state_ptr->proc_size), &(proc_state_ptr->proc_resident));
157 proc_state_ptr->proc_resident = proc_state_ptr->proc_resident * getpagesize();
158 if(s == 'S') proc_state_ptr->state = SLEEPING;
159 if(s == 'R') proc_state_ptr->state = RUNNING;
160 if(s == 'Z') proc_state_ptr->state = ZOMBIE;
161 if(s == 'T') proc_state_ptr->state = STOPPED;
162 if(s == 'D') proc_state_ptr->state = STOPPED;
163
164 /* pa_name[0] should = '(' */
165 ptr = strchr(&ps_name[1], ')');
166 if(ptr !=NULL) *ptr='\0';
167 proc_state_ptr->process_name = strdup(&ps_name[1]);
168
169 /* Need to do cpu */
170
171
172 /* proctitle */
173 snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/cmdline", dir_entry->d_name);
174
175 if((fn=open(filename, O_RDONLY)) == -1){
176 /* Open failed.. Process since vanished, or the path was too long.
177 * Ah well, move onwards to the next one */
178 continue;
179 }
180 #define PSARG_START_SIZE 128
181 if(psargs == NULL){
182 psargs = malloc(PSARG_START_SIZE);
183 psarg_size = PSARG_START_SIZE;
184 }
185 ptr = psargs;
186 t_read = 0;
187 toread = psarg_size;
188 while((size = read(fn, ptr, toread)) == toread){
189 psargs = realloc(psargs, (psarg_size + PSARG_START_SIZE));
190 ptr = psargs+psarg_size;
191 t_read = psarg_size;
192 psarg_size+=PSARG_START_SIZE;
193 toread = PSARG_START_SIZE;
194 }
195 if(size != -1) t_read+=size;
196
197 ptr = psargs;
198 for(x=0; x<t_read; x++){
199 if (*ptr == '\0') *ptr = ' ';
200 ptr++;
201 }
202 /* for safety sake */
203 psargs[t_read] = '\0';
204
205 proc_state_ptr->proctitle = strdup(psargs);
206
207 #endif
208
209 proc_state_size++;
210
211 fclose(f);
212 }
213 closedir(proc_dir);
214 #endif
215
216 #ifdef ALLBSD
217 mib[0] = CTL_KERN;
218 mib[1] = KERN_PROC;
219 mib[2] = KERN_PROC_ALL;
220
221 if(sysctl(mib, 3, NULL, &size, NULL, 0) < 0) {
222 return NULL;
223 }
224
225 procs = size / sizeof(struct kinfo_proc);
226
227 kp_stats = malloc(size);
228 if(kp_stats == NULL) {
229 return NULL;
230 }
231
232 if(sysctl(mib, 3, kp_stats, &size, NULL, 0) < 0) {
233 free(kp_stats);
234 return NULL;
235 }
236
237 #if (defined(FREEBSD) && !defined(FREEBSD5)) || defined(DFBSD)
238 kvmd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL);
239 #endif
240
241 for (i = 0; i < procs; i++) {
242 /* replace with something more sensible */
243 proc_state = realloc(proc_state,
244 (1+proc_state_size)*sizeof(proc_state_t));
245 if(proc_state == NULL ) {
246 return NULL;
247 }
248 proc_state_ptr = proc_state+proc_state_size;
249
250 #ifdef FREEBSD5
251 proc_state_ptr->process_name =
252 strdup(kp_stats[i].ki_comm);
253 #elif defined(DFBSD)
254 proc_state_ptr->process_name =
255 strdup(kp_stats[i].kp_thread.td_comm);
256 #else
257 proc_state_ptr->process_name =
258 strdup(kp_stats[i].kp_proc.p_comm);
259 #endif
260
261 #if defined(FREEBSD5) || defined(NETBSD) || defined(OPENBSD)
262 size = sizeof(buflen);
263
264 #ifdef FREEBSD5
265 if(sysctlbyname("kern.ps_arg_cache_limit", &buflen, &size, NULL, 0) < 0) {
266 return NULL;
267 }
268 #else
269 mib[1] = KERN_ARGMAX;
270
271 if(sysctl(mib, 2, &buflen, &size, NULL, 0) < 0) {
272 return NULL;
273 }
274 #endif
275
276 proctitle = malloc(buflen);
277 if(proctitle == NULL) {
278 return NULL;
279 }
280
281 size = buflen;
282
283 #ifdef FREEBSD5
284 mib[2] = KERN_PROC_ARGS;
285 mib[3] = kp_stats[i].ki_pid;
286 #else
287 mib[1] = KERN_PROC_ARGS;
288 mib[2] = kp_stats[i].kp_proc.p_pid;
289 mib[3] = KERN_PROC_ARGV;
290 #endif
291
292 if(sysctl(mib, 4, proctitle, &size, NULL, 0) < 0) {
293 free(proctitle);
294 proc_state_ptr->proctitle = NULL;
295 }
296 else if(size > 0) {
297 proc_state_ptr->proctitle = malloc(size);
298 if(proc_state_ptr->proctitle == NULL) {
299 return NULL;
300 }
301 p = proctitle;
302 proc_state_ptr->proctitle[0] = NULL;
303 do {
304 strncat(proc_state_ptr->proctitle, p, strlen(p));
305 strncat(proc_state_ptr->proctitle, " ", 1);
306 p += strlen(p) + 1;
307 } while (p < proctitle + size);
308 free(proctitle);
309 proc_state_ptr->proctitle[strlen(proc_state_ptr->proctitle)-1] = NULL;
310 }
311 else {
312 free(proctitle);
313 proc_state_ptr->proctitle = NULL;
314 }
315 #else
316 if(kvmd != NULL) {
317 args = kvm_getargv(kvmd, &(kp_stats[i]), 0);
318 if(args != NULL) {
319 alloc = 1;
320 proctitle = malloc(alloc);
321 if(proctitle == NULL) {
322 return NULL;
323 }
324 while(*args != NULL) {
325 if(strlen(proctitle) + strlen(*args) >= alloc) {
326 alloc = (alloc + strlen(*args)) << 1;
327 proctitle = realloc(proctitle, alloc);
328 if(proctitle == NULL) {
329 return NULL;
330 }
331 }
332 strncat(proctitle, *args, strlen(*args));
333 strncat(proctitle, " ", 1);
334 args++;
335 }
336 /* remove trailing space */
337 proctitle[strlen(proctitle)-1] = NULL;
338 proc_state_ptr->proctitle = proctitle;
339 }
340 else {
341 proc_state_ptr->proctitle = NULL;
342 }
343 }
344 else {
345 proc_state_ptr->proctitle = NULL;
346 }
347 #endif
348
349 #ifdef FREEBSD5
350 proc_state_ptr->pid = kp_stats[i].ki_pid;
351 proc_state_ptr->parent = kp_stats[i].ki_ppid;
352 proc_state_ptr->pgid = kp_stats[i].ki_pgid;
353 #else
354 proc_state_ptr->pid = kp_stats[i].kp_proc.p_pid;
355 proc_state_ptr->parent = kp_stats[i].kp_eproc.e_ppid;
356 proc_state_ptr->pgid = kp_stats[i].kp_eproc.e_pgid;
357 #endif
358
359 #ifdef FREEBSD5
360 proc_state_ptr->uid = kp_stats[i].ki_ruid;
361 proc_state_ptr->euid = kp_stats[i].ki_uid;
362 proc_state_ptr->gid = kp_stats[i].ki_rgid;
363 proc_state_ptr->egid = kp_stats[i].ki_svgid;
364 #elif defined(DFBSD)
365 proc_state_ptr->uid = kp_stats[i].kp_eproc.e_ucred.cr_ruid;
366 proc_state_ptr->euid = kp_stats[i].kp_eproc.e_ucred.cr_svuid;
367 proc_state_ptr->gid = kp_stats[i].kp_eproc.e_ucred.cr_rgid;
368 proc_state_ptr->egid = kp_stats[i].kp_eproc.e_ucred.cr_svgid;
369 #else
370 proc_state_ptr->uid = kp_stats[i].kp_eproc.e_pcred.p_ruid;
371 proc_state_ptr->euid = kp_stats[i].kp_eproc.e_pcred.p_svuid;
372 proc_state_ptr->gid = kp_stats[i].kp_eproc.e_pcred.p_rgid;
373 proc_state_ptr->egid = kp_stats[i].kp_eproc.e_pcred.p_svgid;
374 #endif
375
376 #ifdef FREEBSD5
377 proc_state_ptr->proc_size = kp_stats[i].ki_size;
378 /* This is in pages */
379 proc_state_ptr->proc_resident =
380 kp_stats[i].ki_rssize * getpagesize();
381 /* This is in microseconds */
382 proc_state_ptr->time_spent = kp_stats[i].ki_runtime / 1000000;
383 proc_state_ptr->cpu_percent =
384 ((double)kp_stats[i].ki_pctcpu / FSCALE) * 100.0;
385 proc_state_ptr->nice = kp_stats[i].ki_nice;
386 #else
387 proc_state_ptr->proc_size =
388 kp_stats[i].kp_eproc.e_vm.vm_map.size;
389 /* This is in pages */
390 proc_state_ptr->proc_resident =
391 kp_stats[i].kp_eproc.e_vm.vm_rssize * getpagesize();
392 #if defined(NETBSD) || defined(OPENBSD)
393 proc_state_ptr->time_spent =
394 kp_stats[i].kp_proc.p_rtime.tv_sec;
395 #elif defined(DFBSD)
396 proc_state_ptr->time_spent =
397 ( kp_stats[i].kp_thread.td_uticks +
398 kp_stats[i].kp_thread.td_sticks +
399 kp_stats[i].kp_thread.td_iticks ) / 1000000;
400 #else
401 /* This is in microseconds */
402 proc_state_ptr->time_spent =
403 kp_stats[i].kp_proc.p_runtime / 1000000;
404 #endif
405 proc_state_ptr->cpu_percent =
406 ((double)kp_stats[i].kp_proc.p_pctcpu / FSCALE) * 100.0;
407 proc_state_ptr->nice = kp_stats[i].kp_proc.p_nice;
408 #endif
409
410 #ifdef FREEBSD5
411 switch (kp_stats[i].ki_stat) {
412 #else
413 switch (kp_stats[i].kp_proc.p_stat) {
414 #endif
415 case SIDL:
416 case SRUN:
417 #ifdef SONPROC
418 case SONPROC: /* NetBSD */
419 #endif
420 proc_state_ptr->state = RUNNING;
421 break;
422 case SSLEEP:
423 #ifdef SWAIT
424 case SWAIT: /* FreeBSD 5 */
425 #endif
426 #ifdef SLOCK
427 case SLOCK: /* FreeBSD 5 */
428 #endif
429 proc_state_ptr->state = SLEEPING;
430 break;
431 case SSTOP:
432 proc_state_ptr->state = STOPPED;
433 break;
434 case SZOMB:
435 #ifdef SDEAD
436 case SDEAD: /* OpenBSD & NetBSD */
437 #endif
438 proc_state_ptr->state = ZOMBIE;
439 break;
440 default:
441 proc_state_ptr->state = UNKNOWN;
442 break;
443 }
444 proc_state_size++;
445 }
446
447 free(kp_stats);
448 #endif
449
450 *ps = proc_state;
451 return proc_state_size;
452 }
453
454 process_stat_t *get_process_stats() {
455 static process_stat_t process_stat;
456 proc_state_t *ps;
457 int ps_size, x;
458
459 process_stat.sleeping = 0;
460 process_stat.running = 0;
461 process_stat.zombie = 0;
462 process_stat.stopped = 0;
463 process_stat.total = 0;
464
465 ps_size = get_proc_snapshot(&ps);
466
467 if(ps_size == NULL) {
468 return NULL;
469 }
470
471 for(x = 0; x < ps_size; x++) {
472 switch (ps->state) {
473 /* currently no mapping for UNKNOWN in process_stat_t */
474 case RUNNING:
475 process_stat.running++;
476 break;
477 case SLEEPING:
478 process_stat.sleeping++;
479 break;
480 case STOPPED:
481 process_stat.stopped++;
482 break;
483 case ZOMBIE:
484 process_stat.zombie++;
485 break;
486 }
487 ps++;
488 }
489
490 process_stat.total = ps_size;
491
492 return &process_stat;
493 }