sys-proctable 0.7.6-i586-linux

Sign up to get free protection for your applications and to get access to all the features.
data/ext/linux/linux.c ADDED
@@ -0,0 +1,320 @@
1
+ /***************************************************************
2
+ * linux.c (proctable.c)
3
+ *
4
+ * Linux specific code for the Ruby ps extension. Some code
5
+ * has been copied directly from Dan Urist's Proc::ProcessTable
6
+ * Perl module.
7
+ *
8
+ * Author: Daniel J. Berger
9
+ **************************************************************/
10
+ #include "ruby.h"
11
+ #include "linux.h"
12
+ #include "version.h"
13
+
14
+ #ifdef __cplusplus
15
+ extern "C"
16
+ {
17
+ #endif
18
+
19
+ VALUE sProc, cProcError;
20
+
21
+ /*
22
+ * Converts a proc_info struct into a Ruby struct.
23
+ */
24
+ static VALUE proctable_get_process(struct proc_info *p, VALUE env_hash) {
25
+ return rb_struct_new(sProc,
26
+ rb_str_new2(p->cmdline),
27
+ rb_str_new2(p->cwd),
28
+ rb_str_new2(p->exe),
29
+ INT2FIX(p->pid),
30
+ rb_str_new2(p->name),
31
+ INT2FIX(p->uid),
32
+ INT2FIX(p->euid),
33
+ INT2FIX(p->gid),
34
+ INT2FIX(p->egid),
35
+ rb_str_new2(p->comm),
36
+ rb_str_new(p->state,1),
37
+ INT2FIX(p->ppid),
38
+ INT2FIX(p->pgrp),
39
+ INT2FIX(p->session),
40
+ INT2FIX(p->tty_num),
41
+ INT2FIX(p->tpgid),
42
+ ULONG2NUM(p->flags),
43
+ ULONG2NUM(p->minflt),
44
+ ULONG2NUM(p->cminflt),
45
+ ULONG2NUM(p->majflt),
46
+ ULONG2NUM(p->cmajflt),
47
+ ULONG2NUM(p->utime),
48
+ ULONG2NUM(p->stime),
49
+ LONG2FIX(p->cutime),
50
+ LONG2FIX(p->cstime),
51
+ LONG2FIX(p->priority),
52
+ LONG2FIX(p->nice),
53
+ LONG2FIX(p->itrealvalue),
54
+ ULONG2NUM(boot_time() + (p->starttime / CLK_TCK)),
55
+ ULONG2NUM(p->vsize),
56
+ LONG2FIX(p->rss),
57
+ ULONG2NUM(p->rlim),
58
+ ULONG2NUM(p->startcode),
59
+ ULONG2NUM(p->endcode),
60
+ ULONG2NUM(p->startstack),
61
+ ULONG2NUM(p->kstkesp),
62
+ ULONG2NUM(p->kstkeip),
63
+ ULONG2NUM(p->signal),
64
+ ULONG2NUM(p->blocked),
65
+ ULONG2NUM(p->sigignore),
66
+ ULONG2NUM(p->sigcatch),
67
+ ULONG2NUM(p->wchan),
68
+ ULONG2NUM(p->nswap),
69
+ ULONG2NUM(p->cnswap),
70
+ INT2FIX(p->exit_signal),
71
+ INT2FIX(p->processor),
72
+ env_hash
73
+ );
74
+ }
75
+
76
+ /*
77
+ * call-seq:
78
+ * ProcTable.ps(pid=nil)
79
+ * ProcTable.ps(pid=nil){ |ps| ... }
80
+ *
81
+ * In block form, yields a ProcTableStruct for each process entry that you
82
+ * have rights to. This method returns an array of ProcTableStruct's in
83
+ * non-block form.
84
+ *
85
+ * If a +pid+ is provided, then only a single ProcTableStruct is yielded or
86
+ * returned, or nil if no process information is found for that +pid+.
87
+ */
88
+ static VALUE proctable_ps(int argc, VALUE *argv, VALUE klass){
89
+ DIR* dir_ptr;
90
+ FILE* file_ptr;
91
+ struct dirent* dirent_ptr;
92
+ struct proc_info p;
93
+ char pathbuf[NAME_MAX];
94
+ int temp, num_scanned;
95
+ int pid = -1;
96
+
97
+ VALUE v_pid = Qnil;
98
+ VALUE v_array = Qnil;
99
+ VALUE v_pstruct = Qnil;
100
+ VALUE v_env = rb_hash_new();
101
+
102
+ rb_scan_args(argc, argv, "01", &v_pid);
103
+
104
+ if((dir_ptr = opendir("/proc")) == NULL)
105
+ rb_raise(cProcError, "/proc filesystem unreadable");
106
+
107
+ if(!NIL_P(v_pid))
108
+ pid = NUM2INT(v_pid);
109
+
110
+ if(!rb_block_given_p())
111
+ v_array = rb_ary_new();
112
+
113
+ /* Iterate through all the (numeric) process entries uner /proc */
114
+ while((dirent_ptr = readdir(dir_ptr)) != NULL){
115
+
116
+ /* Only look at this file if it's a proc id; that is, all numbers */
117
+ if( strtok(dirent_ptr->d_name, "0123456789") != NULL )
118
+ continue;
119
+
120
+ if(!NIL_P(v_pid)){
121
+ if(atoi(dirent_ptr->d_name) != pid){
122
+ continue;
123
+ }
124
+ }
125
+
126
+ /* Get the /proc/PID/cmdline info */
127
+ sprintf(pathbuf, "%s%s%s", "/proc/", dirent_ptr->d_name, "/cmdline");
128
+ if( (file_ptr = fopen(pathbuf, "r")) != NULL ){
129
+ size_t got;
130
+ if((got = fread(p.cmdline, sizeof(char), ARG_MAX, file_ptr)) > 0 ){
131
+ size_t i;
132
+
133
+ /* Replace \0 characters with spaces */
134
+ for(i = 0; i < got; i++){
135
+ if(p.cmdline[i] == '\0'){
136
+ p.cmdline[i] = ' ';
137
+ }
138
+ }
139
+
140
+ p.cmdline[got] = '\0';
141
+ trimr(p.cmdline); /* Trim trailing whitespace */
142
+ }
143
+ fclose(file_ptr);
144
+ }
145
+
146
+ /* Get the /proc/PID/exe info (which is a link) */
147
+ sprintf(pathbuf, "%s%s%s", "/proc/", dirent_ptr->d_name, "/exe");
148
+ memset(p.exe, 0, sizeof(p.exe));
149
+
150
+ if(readlink(pathbuf, p.exe, sizeof(p.exe)) == -1){ /* Ignore */ }
151
+
152
+ /* Get the /proc/PID/cwd info (which is a link) */
153
+ sprintf(pathbuf, "%s%s%s", "/proc/", dirent_ptr->d_name, "/cwd");
154
+ memset(p.cwd, 0, sizeof(p.cwd));
155
+
156
+ if(readlink(pathbuf, p.cwd, sizeof(p.cwd)) == -1){ /* Ignore */ }
157
+
158
+ /* Get name, uid, euid, gid and gid info out of /proc/PID/status */
159
+ sprintf(pathbuf, "%s%s%s", "/proc/", dirent_ptr->d_name, "/status");
160
+
161
+ if((file_ptr = fopen(pathbuf,"r")) != NULL ){
162
+ char line[ARG_MAX];
163
+ char dummy[ARG_MAX];
164
+
165
+ while(fgets(line, ARG_MAX - 1, file_ptr) != NULL){
166
+ if(!strncmp("Name", line, strlen("Name"))) {
167
+ sscanf(line, "%s %s", dummy, p.name);
168
+ }
169
+ else if(!strncmp("Uid", line, strlen("Uid"))){
170
+ sscanf(line, "%s %d %d", dummy, &p.uid, &p.euid);
171
+ }
172
+ else if (!strncmp("Gid", line, strlen("Gid"))) {
173
+ sscanf(line, "%s %d %d", dummy, &p.gid, &p.egid);
174
+ }
175
+ }
176
+ fclose(file_ptr);
177
+ }
178
+
179
+ /* Get the /proc/PID/stat info */
180
+ sprintf(pathbuf,"%s%s%s","/proc/",dirent_ptr->d_name,"/stat");
181
+ if((file_ptr = fopen(pathbuf,"r")) != NULL){
182
+ num_scanned = fscanf(file_ptr,
183
+ "%d %s %s %d %d\
184
+ %d %d %d %lu %lu\
185
+ %lu %lu %lu %lu %lu\
186
+ %ld %ld %ld %ld %d\
187
+ %ld %lu %lu %ld %lu\
188
+ %lu %lu %lu %lu %lu\
189
+ %lu %lu %lu %lu %lu\
190
+ %lu %lu %d %d",
191
+ &p.pid, p.comm, p.state, &p.ppid, &p.pgrp,
192
+ &p.session, &p.tty_num, &p.tpgid, &p.flags, &p.minflt,
193
+ &p.cminflt, &p.majflt, &p.cmajflt, &p.utime, &p.stime,
194
+ &p.cutime, &p.cstime, &p.priority, &p.nice, &temp,
195
+ &p.itrealvalue,&p.starttime, &p.vsize, &p.rss, &p.rlim,
196
+ &p.startcode, &p.endcode, &p.startstack, &p.kstkesp, &p.kstkeip,
197
+ &p.signal, &p.blocked, &p.sigignore, &p.sigcatch, &p.wchan,
198
+ &p.nswap, &p.cnswap, &p.exit_signal,&p.processor
199
+ );
200
+
201
+ fclose(file_ptr);
202
+
203
+ if(num_scanned < 39)
204
+ rb_sys_fail("fscanf");
205
+ }
206
+
207
+ /* Get rid of the parens in the comm field */
208
+ strcpy(p.comm, strtok(p.comm,"()"));
209
+
210
+ /**********************************************************************
211
+ * Get the environ string out or /proc/PID/environ and make a hash
212
+ * out of it
213
+ **********************************************************************/
214
+ sprintf(pathbuf, "%s%s%s", "/proc/", dirent_ptr->d_name, "/environ");
215
+ if((file_ptr = fopen( pathbuf, "r" )) != NULL){
216
+
217
+ int count;
218
+ char env_buf[ARG_MAX];
219
+ char key[MAX_ENV_NAME];
220
+ char *next_var, *value;
221
+
222
+ /* Get char count */
223
+ count = fread(env_buf, sizeof(char), ARG_MAX, file_ptr);
224
+ next_var = env_buf;
225
+
226
+ while(next_var < env_buf + count){
227
+ value = strchr(next_var,'=');
228
+ /***************************************************************
229
+ * It's possible some environ files contain data that's not in
230
+ * the KEY=value format. For such cases, we'll just set the
231
+ * string as the key, and set the corresponding value to nil.
232
+ ***************************************************************/
233
+ if(NULL == value){
234
+ rb_hash_aset(v_env, rb_str_new2(next_var), Qnil);
235
+ break;
236
+ }
237
+ strncpy(key, next_var, value - next_var);
238
+ key[value-next_var] = '\0';
239
+
240
+ ++value;
241
+ rb_hash_aset(v_env, rb_str_new2(key), rb_str_new2(value));
242
+ next_var += strlen(next_var) + 1;
243
+ }
244
+
245
+ fclose(file_ptr);
246
+ }
247
+
248
+ /* If cmdline is empty, use comm instead */
249
+ if(0 == strlen(p.cmdline))
250
+ strcpy(p.cmdline, p.comm);
251
+
252
+ v_pstruct = proctable_get_process(&p, v_env);
253
+
254
+ if(rb_block_given_p())
255
+ rb_yield(v_pstruct);
256
+ else
257
+ rb_ary_push(v_array, v_pstruct);
258
+ }
259
+
260
+ closedir(dir_ptr);
261
+
262
+ /* If a process ID was provided and found, return a single struct */
263
+ if(!NIL_P(v_pid))
264
+ return v_pstruct;
265
+
266
+ return v_array; /* nil if block was provided */
267
+ }
268
+
269
+ /*
270
+ * call-seq:
271
+ * ProcTable.fields
272
+ *
273
+ * Returns an array of fields that each ProcTableStruct will contain. This
274
+ * may be useful if you want to know in advance what fields are available
275
+ * without having to perform at least one read of the /proc table.
276
+ */
277
+ static VALUE proctable_fields(VALUE klass){
278
+ unsigned int i;
279
+ VALUE rbFarray = rb_ary_new();
280
+
281
+ for(i = 0; i < sizeof(fields)/sizeof(fields[0]); i++)
282
+ rb_ary_push(rbFarray,rb_str_new2(fields[i]));
283
+
284
+ return rbFarray;
285
+ }
286
+
287
+ /*
288
+ * A Ruby interface for gathering process table information.
289
+ */
290
+ void Init_proctable()
291
+ {
292
+ VALUE mSys, cProcTable;
293
+
294
+ /* Modules and Classes */
295
+ mSys = rb_define_module("Sys");
296
+ cProcTable = rb_define_class_under(mSys, "ProcTable", rb_cObject);
297
+ cProcError = rb_define_class_under(mSys, "ProcTableError",rb_eStandardError);
298
+
299
+ /* Class Methods */
300
+ rb_define_singleton_method(cProcTable, "fields", proctable_fields, 0);
301
+ rb_define_singleton_method(cProcTable, "ps", proctable_ps, -1);
302
+
303
+ /* Constants */
304
+ rb_define_const(cProcTable,"VERSION",rb_str_new2(SYS_PROCTABLE_VERSION));
305
+
306
+ /* Structs */
307
+ sProc = rb_struct_define("ProcTableStruct","cmdline","cwd","exe","pid",
308
+ "name", "uid", "euid", "gid", "egid", "comm","state","ppid",
309
+ "pgrp","session","tty_num","tpgid","flags","minflt","cminflt","majflt",
310
+ "cmajflt","utime","stime","cutime","cstime","priority","nice",
311
+ "itrealvalue","starttime","vsize","rss","rlim","startcode","endcode",
312
+ "startstack","kstkesp","kstkeip","signal","blocked","sigignore",
313
+ "sigcatch","wchan","nswap","cnswap","exit_signal","processor","environ",
314
+ NULL
315
+ );
316
+ }
317
+
318
+ #ifdef __cplusplus
319
+ }
320
+ #endif
data/ext/linux/linux.h ADDED
@@ -0,0 +1,158 @@
1
+ /************************************************************************
2
+ * linux.h
3
+ *
4
+ * Linux specific information, including struct layout.
5
+ ************************************************************************/
6
+ #include <dirent.h>
7
+ #include <linux/limits.h>
8
+ #include <unistd.h>
9
+ #include <string.h>
10
+ #include <ctype.h>
11
+
12
+ #ifndef CLK_TCK
13
+ #define CLK_TCK sysconf(_SC_CLK_TCK)
14
+ #endif
15
+
16
+ #define MAX_ENV_NAME 128
17
+ #define BTIME_BUF 1024
18
+
19
+ /****************************************************************************
20
+ * Note that the 20th field (between nice and itrealvalue) is not included
21
+ * in our struct. It is a simple placeholder that was hardcoded for a
22
+ * removed field. See the proc man page for more details.
23
+ *
24
+ * Also note that the environ attribute is not part of this struct, mostly
25
+ * because I wouldn't know what type to set it to. It *is* part of the ruby
26
+ * struct, however.
27
+ ****************************************************************************/
28
+ struct proc_info{
29
+ char cmdline[ARG_MAX];
30
+ char cwd[ARG_MAX];
31
+ char exe[ARG_MAX];
32
+ int pid;
33
+ char name[ARG_MAX];
34
+ int uid;
35
+ int euid;
36
+ int gid;
37
+ int egid;
38
+ char comm[ARG_MAX];
39
+ char state[1];
40
+ int ppid;
41
+ int pgrp;
42
+ int session;
43
+ int tty_num;
44
+ int tpgid;
45
+ unsigned long flags;
46
+ unsigned long minflt;
47
+ unsigned long cminflt;
48
+ unsigned long majflt;
49
+ unsigned long cmajflt;
50
+ unsigned long utime;
51
+ unsigned long stime;
52
+ long cutime;
53
+ long cstime;
54
+ long priority;
55
+ long nice;
56
+ long itrealvalue;
57
+ unsigned long starttime;
58
+ unsigned long vsize;
59
+ long rss;
60
+ unsigned long rlim;
61
+ unsigned long startcode;
62
+ unsigned long endcode;
63
+ unsigned long startstack;
64
+ unsigned long kstkesp;
65
+ unsigned long kstkeip;
66
+ unsigned long signal;
67
+ unsigned long blocked;
68
+ unsigned long sigignore;
69
+ unsigned long sigcatch;
70
+ unsigned long wchan;
71
+ unsigned long nswap;
72
+ unsigned long cnswap;
73
+ int exit_signal;
74
+ int processor;
75
+ };
76
+
77
+ /*****************************************************************************
78
+ * This array's only purpose is for the 'fields()' class method. If there's
79
+ * a way to report fields out of our defined struct (sProc), please tell me
80
+ * how!
81
+ *****************************************************************************/
82
+ char *fields[] = {
83
+ "cmdline",
84
+ "cwd",
85
+ "exe",
86
+ "pid",
87
+ "name",
88
+ "uid",
89
+ "euid",
90
+ "gid",
91
+ "egid",
92
+ "comm",
93
+ "state",
94
+ "ppid",
95
+ "pgrp",
96
+ "session",
97
+ "tty_num",
98
+ "tpgid",
99
+ "flags",
100
+ "minflt",
101
+ "cminflt",
102
+ "majflt",
103
+ "cmajflt",
104
+ "utime",
105
+ "stime",
106
+ "cutime",
107
+ "cstime",
108
+ "priority",
109
+ "nice",
110
+ "itrealvalue",
111
+ "starttime",
112
+ "vsize",
113
+ "rss",
114
+ "rlim",
115
+ "startcode",
116
+ "endcode",
117
+ "startstack",
118
+ "kstkesp",
119
+ "kstkeip",
120
+ "signal",
121
+ "blocked",
122
+ "sigignore",
123
+ "sigcatch",
124
+ "wchan",
125
+ "nswap",
126
+ "cnswap",
127
+ "exit_signal",
128
+ "processor",
129
+ "environ"
130
+ };
131
+
132
+ /* Helper function to remove trailing whitespace. Use for cmdline info */
133
+ void trimr(char* s){
134
+ char* p = s + strlen(s);
135
+ while(--p >= s && isspace(*p))
136
+ ;
137
+
138
+ *++p = 0;
139
+ }
140
+
141
+ /* Helper function to get the boot time */
142
+ unsigned long boot_time(){
143
+ FILE* fp;
144
+ char buf[BTIME_BUF];
145
+ unsigned long btime = 0;
146
+
147
+ if((fp = fopen( "/proc/stat", "r" )) != NULL){
148
+ while(!feof(fp)){
149
+ if(fscanf(fp,"btime %ld", &btime) == 1)
150
+ break;
151
+ if(fgets(buf, BTIME_BUF, fp) == NULL)
152
+ break;
153
+ }
154
+ fclose(fp);
155
+ }
156
+
157
+ return btime;
158
+ }