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

# User Rev Content
1 tdb 1.17 /*
2 pajs 1.1 * i-scream central monitoring system
3 tdb 1.8 * http://www.i-scream.org
4 tdb 1.17 * Copyright (C) 2000-2004 i-scream
5 pajs 1.1 *
6 tdb 1.17 * 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 pajs 1.1 *
11 tdb 1.17 * This library is distributed in the hope that it will be useful,
12 pajs 1.1 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 tdb 1.17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14     * Lesser General Public License for more details.
15 pajs 1.1 *
16 tdb 1.17 * 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 tdb 1.18 *
21 tdb 1.37 * $Id: process_stats.c,v 1.36 2004/04/04 16:12:59 tdb Exp $
22 pajs 1.1 */
23    
24     #ifdef HAVE_CONFIG_H
25     #include "config.h"
26     #endif
27    
28 pajs 1.6 #include "statgrab.h"
29     #if defined(SOLARIS) || defined(LINUX)
30 pajs 1.1 #include <stdio.h>
31     #include <stdlib.h>
32 pajs 1.5 #include <sys/types.h>
33     #include <dirent.h>
34     #include <string.h>
35 pajs 1.6 #endif
36 pajs 1.1
37     #ifdef SOLARIS
38     #include <procfs.h>
39     #include <limits.h>
40 pajs 1.5 #define PROC_LOCATION "/proc"
41     #define MAX_FILE_LENGTH PATH_MAX
42     #endif
43     #ifdef LINUX
44 ats 1.16 #include <limits.h>
45 pajs 1.25 #include <unistd.h>
46     #include <sys/stat.h>
47     #include <fcntl.h>
48 pajs 1.1 #define PROC_LOCATION "/proc"
49     #define MAX_FILE_LENGTH PATH_MAX
50     #endif
51 ats 1.13 #ifdef ALLBSD
52 tdb 1.19 #include <stdlib.h>
53 pajs 1.6 #include <sys/param.h>
54     #include <sys/sysctl.h>
55 tdb 1.22 #if defined(FREEBSD) || defined(DFBSD)
56     #include <sys/user.h>
57     #else
58 tdb 1.19 #include <sys/proc.h>
59 ats 1.13 #endif
60 tdb 1.29 #include <string.h>
61     #include <paths.h>
62     #include <fcntl.h>
63     #include <limits.h>
64     #include <kvm.h>
65 pajs 1.6 #endif
66 pajs 1.1
67 pajs 1.23 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 tdb 1.26 #ifdef ALLBSD
72 tdb 1.37 int mib[4];
73 tdb 1.26 size_t size;
74     struct kinfo_proc *kp_stats;
75 tdb 1.29 int procs, i, alloc;
76 tdb 1.37 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 tdb 1.29 static kvm_t *kvmd;
84     char **args;
85 tdb 1.37 #endif
86 tdb 1.26 #endif
87 pajs 1.24 #if defined(SOLARIS) || defined(LINUX)
88 pajs 1.23 DIR *proc_dir;
89     struct dirent *dir_entry;
90     char filename[MAX_FILE_LENGTH];
91     FILE *f;
92 pajs 1.24 #ifdef SOLARIS
93 pajs 1.5 psinfo_t process_info;
94 pajs 1.24 #endif
95 pajs 1.25 #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 pajs 1.1
110 pajs 1.23 if((proc_dir=opendir(PROC_LOCATION))==NULL){
111 pajs 1.25 return -1;
112 pajs 1.23 }
113    
114     while((dir_entry=readdir(proc_dir))!=NULL){
115     if(atoi(dir_entry->d_name) == 0) continue;
116    
117 pajs 1.24 #ifdef SOLARIS
118 pajs 1.23 snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/psinfo", dir_entry->d_name);
119 pajs 1.24 #endif
120 pajs 1.25 #ifdef LINUX
121     snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/stat", dir_entry->d_name);
122     #endif
123 pajs 1.23 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 pajs 1.24 #ifdef SOLARIS
129 pajs 1.23 fread(&process_info, sizeof(psinfo_t), 1, f);
130 pajs 1.25 #endif
131 pajs 1.1
132 pajs 1.23 proc_state = realloc(proc_state, (1+proc_state_size)*sizeof(proc_state_t));
133     proc_state_ptr = proc_state+proc_state_size;
134 pajs 1.25 #ifdef SOLARIS
135 pajs 1.23 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 pajs 1.24
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 pajs 1.25 #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 pajs 1.24 #endif
208 pajs 1.23
209     proc_state_size++;
210 pajs 1.1
211 pajs 1.23 fclose(f);
212     }
213     closedir(proc_dir);
214 tdb 1.26 #endif
215    
216     #ifdef ALLBSD
217 tdb 1.36 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 tdb 1.33
237 tdb 1.37 #if (defined(FREEBSD) && !defined(FREEBSD5)) || defined(DFBSD)
238 tdb 1.29 kvmd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL);
239 tdb 1.33 #endif
240 tdb 1.26
241     for (i = 0; i < procs; i++) {
242 tdb 1.29 /* replace with something more sensible */
243 tdb 1.30 proc_state = realloc(proc_state,
244     (1+proc_state_size)*sizeof(proc_state_t));
245 tdb 1.29 if(proc_state == NULL ) {
246     return NULL;
247     }
248 tdb 1.26 proc_state_ptr = proc_state+proc_state_size;
249 tdb 1.30
250     #ifdef FREEBSD5
251     proc_state_ptr->process_name =
252     strdup(kp_stats[i].ki_comm);
253 tdb 1.35 #elif defined(DFBSD)
254     proc_state_ptr->process_name =
255     strdup(kp_stats[i].kp_thread.td_comm);
256 tdb 1.30 #else
257     proc_state_ptr->process_name =
258     strdup(kp_stats[i].kp_proc.p_comm);
259     #endif
260 tdb 1.29
261 tdb 1.37 #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 tdb 1.29 return NULL;
300     }
301 tdb 1.37 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 tdb 1.29 }
332 tdb 1.37 strncat(proctitle, *args, strlen(*args));
333     strncat(proctitle, " ", 1);
334     args++;
335 tdb 1.29 }
336 tdb 1.37 /* 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 tdb 1.29 }
343     }
344     else {
345 tdb 1.37 proc_state_ptr->proctitle = NULL;
346 tdb 1.29 }
347 tdb 1.37 #endif
348 tdb 1.26
349 tdb 1.30 #ifdef FREEBSD5
350 tdb 1.26 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 tdb 1.30 #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 tdb 1.26
359 tdb 1.30 #ifdef FREEBSD5
360 tdb 1.26 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 tdb 1.35 #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 tdb 1.30 #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 tdb 1.26
376 tdb 1.30 #ifdef FREEBSD5
377 tdb 1.26 proc_state_ptr->proc_size = kp_stats[i].ki_size;
378     /* This is in pages */
379 tdb 1.30 proc_state_ptr->proc_resident =
380     kp_stats[i].ki_rssize * getpagesize();
381 tdb 1.28 /* This is in microseconds */
382 tdb 1.26 proc_state_ptr->time_spent = kp_stats[i].ki_runtime / 1000000;
383 tdb 1.30 proc_state_ptr->cpu_percent =
384     ((double)kp_stats[i].ki_pctcpu / FSCALE) * 100.0;
385 tdb 1.26 proc_state_ptr->nice = kp_stats[i].ki_nice;
386 tdb 1.30 #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 tdb 1.34 #if defined(NETBSD) || defined(OPENBSD)
393 tdb 1.33 proc_state_ptr->time_spent =
394     kp_stats[i].kp_proc.p_rtime.tv_sec;
395 tdb 1.35 #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 tdb 1.33 #else
401 tdb 1.36 /* This is in microseconds */
402 tdb 1.30 proc_state_ptr->time_spent =
403     kp_stats[i].kp_proc.p_runtime / 1000000;
404 tdb 1.33 #endif
405 tdb 1.30 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 tdb 1.26
410 tdb 1.30 #ifdef FREEBSD5
411 tdb 1.26 switch (kp_stats[i].ki_stat) {
412 tdb 1.30 #else
413     switch (kp_stats[i].kp_proc.p_stat) {
414     #endif
415 tdb 1.26 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 tdb 1.31 break;
440     default:
441     proc_state_ptr->state = UNKNOWN;
442 tdb 1.26 break;
443     }
444     proc_state_size++;
445     }
446    
447     free(kp_stats);
448     #endif
449    
450 pajs 1.23 *ps = proc_state;
451     return proc_state_size;
452 tdb 1.27 }
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 tdb 1.30 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 tdb 1.27
465     ps_size = get_proc_snapshot(&ps);
466 tdb 1.32
467     if(ps_size == NULL) {
468     return NULL;
469     }
470 tdb 1.27
471     for(x = 0; x < ps_size; x++) {
472     switch (ps->state) {
473 tdb 1.30 /* currently no mapping for UNKNOWN in process_stat_t */
474 tdb 1.27 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 pajs 1.23 }