sys-proctable 0.7.3

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