sys-proctable 1.0.0-universal-darwin → 1.1.0-universal-darwin

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,451 +0,0 @@
1
- /**********************************************************************
2
- * Mac OS X code for sys-proctable Ruby library. *
3
- * *
4
- * Date: 3-Mar-2006 (original submission date) *
5
- * Author: David Felstead (david.felstead at gmail dot com) *
6
- * Based on bsd.c by Daniel J. Berger (djberg96 at gmail dot com) *
7
- *********************************************************************/
8
- #include "ruby.h"
9
- #include <sys/param.h>
10
- #include <sys/stat.h>
11
- #include <sys/sysctl.h>
12
- #include <sys/types.h>
13
- #include <sys/user.h>
14
- #include <errno.h>
15
-
16
- #include <stdio.h>
17
- #include <stdlib.h>
18
- #include <string.h>
19
- #include <unistd.h>
20
-
21
- #define pid_of(pproc) pproc->kp_proc.p_pid
22
-
23
- #define PROC_MIB_LEN 4
24
- #define ARGS_MIB_LEN 3
25
-
26
- #ifndef ARGS_MAX_LEN
27
- #define ARGS_MAX_LEN sysconf(_SC_ARG_MAX)
28
- #endif
29
-
30
- VALUE cProcTableError, sProcStruct;
31
-
32
- int argv_of_pid(int pid, VALUE* v_cmdline, VALUE* v_exe, VALUE* v_environ) {
33
- int mib[3], argmax, nargs, c = 0;
34
- size_t size;
35
- char *procargs, *sp, *np, *cp;
36
- int show_args = 1;
37
-
38
- mib[0] = CTL_KERN;
39
- mib[1] = KERN_ARGMAX;
40
-
41
- size = sizeof(argmax);
42
-
43
- if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1)
44
- goto ERROR_A;
45
-
46
- // Allocate space for the arguments.
47
- procargs = (char *)ruby_xmalloc(argmax);
48
-
49
- /*
50
- * Make a sysctl() call to get the raw argument space of the process.
51
- * The layout is documented in start.s, which is part of the Csu
52
- * project. In summary, it looks like:
53
- *
54
- * /---------------\ 0x00000000
55
- * : :
56
- * : :
57
- * |---------------|
58
- * | argc |
59
- * |---------------|
60
- * | arg[0] |
61
- * |---------------|
62
- * : :
63
- * : :
64
- * |---------------|
65
- * | arg[argc - 1] |
66
- * |---------------|
67
- * | 0 |
68
- * |---------------|
69
- * | env[0] |
70
- * |---------------|
71
- * : :
72
- * : :
73
- * |---------------|
74
- * | env[n] |
75
- * |---------------|
76
- * | 0 |
77
- * |---------------| <-- Beginning of data returned by sysctl() is here.
78
- * | argc |
79
- * |---------------|
80
- * | exec_path |
81
- * |:::::::::::::::|
82
- * | |
83
- * | String area. |
84
- * | |
85
- * |---------------| <-- Top of stack.
86
- * : :
87
- * : :
88
- * \---------------/ 0xffffffff
89
- */
90
- mib[0] = CTL_KERN;
91
- mib[1] = KERN_PROCARGS2;
92
- mib[2] = pid;
93
-
94
- size = (size_t)argmax;
95
-
96
- if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1)
97
- goto ERROR_B;
98
-
99
- memcpy(&nargs, procargs, sizeof(nargs));
100
- cp = procargs + sizeof(nargs);
101
-
102
- // Copy exec_path to ruby String.
103
- *v_exe = rb_str_new2(cp);
104
-
105
- // Skip the saved exec_path.
106
- for (; cp < &procargs[size]; cp++) {
107
- if (*cp == '\0')
108
- break; // End of exec_path reached.
109
- }
110
-
111
- if (cp == &procargs[size])
112
- goto ERROR_B;
113
-
114
- // Skip trailing '\0' characters.
115
- for (; cp < &procargs[size]; cp++){
116
- if (*cp != '\0')
117
- break; // Beginning of first argument reached.
118
- }
119
-
120
- if (cp == &procargs[size])
121
- goto ERROR_B;
122
-
123
- // Save where the argv[0] string starts.
124
- sp = cp;
125
-
126
- /*
127
- * Iterate through the '\0'-terminated strings and convert '\0' to ' '
128
- * until a string is found that has a '=' character in it (or there are
129
- * no more strings in procargs). There is no way to deterministically
130
- * know where the command arguments end and the environment strings
131
- * start, which is why the '=' character is searched for as a heuristic.
132
- */
133
- for (np = NULL; c < nargs && cp < &procargs[size]; cp++) {
134
- if (*cp == '\0') {
135
- c++;
136
-
137
- if (np != NULL)
138
- *np = ' '; // Convert previous '\0'.
139
-
140
- // Note location of current '\0'.
141
- np = cp;
142
-
143
- if (!show_args) {
144
- /*
145
- * Don't convert '\0' characters to ' '.
146
- * However, we needed to know that the
147
- * command name was terminated, which we
148
- * now know.
149
- */
150
- break;
151
- }
152
- }
153
- }
154
-
155
- /*
156
- * sp points to the beginning of the arguments/environment string, and
157
- * np should point to the '\0' terminator for the string.
158
- */
159
- if (np == NULL || np == sp)
160
- goto ERROR_B; // Empty or unterminated string.
161
-
162
- // Make a copy of the string to ruby String.
163
- *v_cmdline = rb_str_new2(sp);
164
-
165
- // Read environment variables to ruby Hash.
166
- *v_environ = rb_hash_new();
167
-
168
- while (cp[0]) {
169
- sp = strsep(&cp, "=");
170
-
171
- if (!sp || !cp)
172
- break;
173
-
174
- rb_hash_aset(*v_environ, rb_str_new2(sp), rb_str_new2(cp));
175
- cp += strlen(cp) + 1;
176
- }
177
-
178
- // Cleanup.
179
- ruby_xfree(procargs);
180
- return 0;
181
-
182
- ERROR_B:
183
- ruby_xfree(procargs);
184
- ERROR_A:
185
- return -1;
186
- }
187
-
188
- /*
189
- * call-seq:
190
- * ProcTable.ps(pid=nil)
191
- * ProcTable.ps(pid=nil){ |ps| ... }
192
- *
193
- * In block form, yields a ProcTableStruct for each process entry that you
194
- * have rights to. This method returns an array of ProcTableStruct's in
195
- * non-block form.
196
- *
197
- * If a +pid+ is provided, then only a single ProcTableStruct is yielded or
198
- * returned, or nil if no process information is found for that +pid+.
199
- */
200
- static VALUE pt_ps(int argc, VALUE* argv, VALUE klass){
201
- int err;
202
- char state[8];
203
- struct kinfo_proc* procs;
204
- VALUE v_pid, v_tty_num, v_tty_dev, v_start_time;
205
- VALUE v_pstruct = Qnil;
206
- VALUE v_array = rb_ary_new();
207
- size_t length, count;
208
- size_t i = 0;
209
- int g;
210
- VALUE v_cmdline, v_exe, v_environ, v_groups;
211
-
212
- // Passed into sysctl call
213
- static const int name_mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
214
-
215
- rb_scan_args(argc, argv, "01", &v_pid);
216
-
217
- // Get size of proc kproc buffer
218
- err = sysctl( (int *) name_mib, PROC_MIB_LEN, NULL, &length, NULL, 0);
219
-
220
- if(err == -1)
221
- rb_raise(cProcTableError, "sysctl: %s", strerror(errno));
222
-
223
- // Populate the kproc buffer
224
- procs = ruby_xmalloc(length);
225
-
226
- err = sysctl( (int *) name_mib, PROC_MIB_LEN, procs, &length, NULL, 0);
227
-
228
- if(err == -1)
229
- rb_raise(cProcTableError, "sysctl: %s", strerror(errno));
230
-
231
- // If we're here, we got our list
232
- count = length / sizeof(struct kinfo_proc);
233
-
234
- for(i = 0; i < count; i++) {
235
- v_tty_num = Qnil;
236
- v_tty_dev = Qnil;
237
- v_start_time = Qnil;
238
-
239
- // If a PID is provided, skip unless the PID matches
240
- if( (!NIL_P(v_pid)) && (procs[i].kp_proc.p_pid != NUM2INT(v_pid)) )
241
- continue;
242
-
243
- // cmdline will be set only if process exists and belongs to current user or
244
- // current user is root
245
- v_cmdline = Qnil;
246
- v_exe = Qnil;
247
- v_environ = Qnil;
248
- argv_of_pid(procs[i].kp_proc.p_pid, &v_cmdline, &v_exe, &v_environ);
249
-
250
- // Get the start time of the process
251
- v_start_time = rb_time_new(
252
- procs[i].kp_proc.p_un.__p_starttime.tv_sec,
253
- procs[i].kp_proc.p_un.__p_starttime.tv_usec
254
- );
255
-
256
- // Get the state of the process
257
- switch(procs[i].kp_proc.p_stat)
258
- {
259
- case SIDL:
260
- strcpy(state, "idle");
261
- break;
262
- case SRUN:
263
- strcpy(state, "run");
264
- break;
265
- case SSLEEP:
266
- strcpy(state, "sleep");
267
- break;
268
- case SSTOP:
269
- strcpy(state, "stop");
270
- break;
271
- case SZOMB:
272
- strcpy(state, "zombie");
273
- break;
274
- default:
275
- strcpy(state, "unknown");
276
- break;
277
- }
278
-
279
- // Get ttynum and ttydev. If ttynum is -1, there is no tty.
280
- if(procs[i].kp_eproc.e_tdev != -1){
281
- v_tty_num = INT2FIX(procs[i].kp_eproc.e_tdev),
282
- v_tty_dev = rb_str_new2(devname(procs[i].kp_eproc.e_tdev, S_IFCHR));
283
- }
284
-
285
- v_groups = rb_ary_new();
286
-
287
- for (g = 0; g < procs[i].kp_eproc.e_ucred.cr_ngroups; ++g)
288
- rb_ary_push(v_groups, INT2FIX(procs[i].kp_eproc.e_ucred.cr_groups[g]));
289
-
290
- v_pstruct = rb_struct_new(
291
- sProcStruct,
292
- INT2FIX(procs[i].kp_proc.p_pid),
293
- INT2FIX(procs[i].kp_eproc.e_ppid),
294
- INT2FIX(procs[i].kp_eproc.e_pgid),
295
- INT2FIX(procs[i].kp_eproc.e_pcred.p_ruid),
296
- INT2FIX(procs[i].kp_eproc.e_pcred.p_rgid),
297
- INT2FIX(procs[i].kp_eproc.e_ucred.cr_uid),
298
- rb_ary_entry(v_groups, 0),
299
- v_groups,
300
- INT2FIX(procs[i].kp_eproc.e_pcred.p_svuid),
301
- INT2FIX(procs[i].kp_eproc.e_pcred.p_svgid),
302
- rb_str_new2(procs[i].kp_proc.p_comm),
303
- rb_str_new2(state),
304
- rb_float_new(procs[i].kp_proc.p_pctcpu),
305
- Qnil,
306
- v_tty_num,
307
- v_tty_dev,
308
- rb_str_new2(procs[i].kp_eproc.e_wmesg),
309
- INT2FIX(procs[i].kp_proc.p_rtime.tv_sec),
310
- INT2FIX(procs[i].kp_proc.p_priority),
311
- INT2FIX(procs[i].kp_proc.p_usrpri),
312
- INT2FIX(procs[i].kp_proc.p_nice),
313
- v_cmdline,
314
- v_exe,
315
- v_environ,
316
- v_start_time,
317
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_maxrss) : Qnil,
318
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_ixrss) : Qnil,
319
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_idrss) : Qnil,
320
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_isrss) : Qnil,
321
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_minflt) : Qnil,
322
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_majflt) : Qnil,
323
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_nswap) : Qnil,
324
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_inblock) : Qnil,
325
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_oublock) : Qnil,
326
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_msgsnd) : Qnil,
327
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_msgrcv) : Qnil,
328
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_nsignals) : Qnil,
329
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_nvcsw) : Qnil,
330
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_nivcsw) : Qnil,
331
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_utime.tv_sec) : Qnil,
332
- (procs[i].kp_proc.p_ru && procs[i].kp_proc.p_stat != 5) ? LONG2NUM(procs[i].kp_proc.p_ru->ru_stime.tv_sec) : Qnil
333
- );
334
-
335
- OBJ_FREEZE(v_pstruct); // This is read-only data
336
-
337
- if(rb_block_given_p())
338
- rb_yield(v_pstruct);
339
- else
340
- rb_ary_push(v_array, v_pstruct);
341
- }
342
-
343
- if(procs)
344
- free(procs);
345
-
346
- if(!rb_block_given_p()){
347
- if(NIL_P(v_pid))
348
- return v_array;
349
- else
350
- return v_pstruct;
351
- }
352
-
353
- return Qnil;
354
- }
355
-
356
- /*
357
- * call-seq:
358
- * ProcTable.fields
359
- *
360
- * Returns an array of fields that each ProcTableStruct will contain. This
361
- * may be useful if you want to know in advance what fields are available
362
- * without having to perform at least one read of the /proc table.
363
- */
364
- static VALUE pt_fields(VALUE klass){
365
- VALUE v_array = rb_ary_new();
366
-
367
- VALUE v_members = rb_struct_s_members(sProcStruct), v_member;
368
- long size = RARRAY_LEN(v_members);
369
- int i;
370
-
371
- for(i = 0; i < size; i++) {
372
- v_member = rb_funcall(rb_ary_entry(v_members, i), rb_intern("to_s"), 0);
373
- rb_ary_push(v_array, v_member);
374
- }
375
-
376
- return v_array;
377
- }
378
-
379
- /*
380
- * A Ruby interface for gathering process table information.
381
- */
382
- void Init_proctable(){
383
- VALUE mSys, cProcTable;
384
-
385
- /* The Sys module serves as a namespace only */
386
- mSys = rb_define_module("Sys");
387
-
388
- /* The ProcTable class encapsulates process table information */
389
- cProcTable = rb_define_class_under(mSys, "ProcTable", rb_cObject);
390
-
391
- /* The Error class typically raised if any of the ProcTable methods fail */
392
- cProcTableError = rb_define_class_under(cProcTable, "Error", rb_eStandardError);
393
-
394
- /* Singleton methods */
395
-
396
- rb_define_singleton_method(cProcTable, "ps", pt_ps, -1);
397
- rb_define_singleton_method(cProcTable, "fields", pt_fields, 0);
398
-
399
- /* There is no constructor */
400
- rb_funcall(cProcTable, rb_intern("private_class_method"), 1, ID2SYM(rb_intern("new")));
401
-
402
- /* 1.0.0: The version of the sys-proctable library */
403
- rb_define_const(cProcTable, "VERSION", rb_str_new2("1.0.0"));
404
-
405
- /* Structs */
406
-
407
- sProcStruct = rb_struct_define("ProcTableStruct",
408
- "pid", /* Process identifier */
409
- "ppid", /* Parent process id */
410
- "pgid", /* Process group id */
411
- "ruid", /* Real user id */
412
- "rgid", /* Real group id */
413
- "euid", /* Effective user id */
414
- "egid", /* Effective group id */
415
- "groups", /* All effective group ids */
416
- "svuid", /* Saved effective user id */
417
- "svgid", /* Saved effective group id */
418
- "comm", /* Command name (15 chars) */
419
- "state", /* Process status */
420
- "pctcpu", /* %cpu for this process during p_swtime */
421
- "oncpu", /* nil */
422
- "tnum", /* Controlling tty dev */
423
- "tdev", /* Controlling tty name */
424
- "wmesg", /* Wchan message */
425
- "rtime", /* Real time */
426
- "priority", /* Process priority */
427
- "usrpri", /* User-priority */
428
- "nice", /* Process "nice" value */
429
- "cmdline", /* Complete command line */
430
- "exe", /* Saved pathname of the executed command */
431
- "environ", /* Hash with process environment variables */
432
- "starttime", /* Process start time */
433
- "maxrss", /* Max resident set size (PL) */
434
- "ixrss", /* Integral shared memory size (NU) */
435
- "idrss", /* Integral unshared data (NU) */
436
- "isrss", /* Integral unshared stack (NU) */
437
- "minflt", /* Page reclaims (NU) */
438
- "majflt", /* Page faults (NU) */
439
- "nswap", /* Swaps (NU) */
440
- "inblock", /* Block input operations (atomic) */
441
- "oublock", /* Block output operations (atomic) */
442
- "msgsnd", /* Messages sent (atomic) */
443
- "msgrcv", /* Messages received (atomic) */
444
- "nsignals", /* Signals received (atomic) */
445
- "nvcsw", /* Voluntary context switches (atomic) */
446
- "nivcsw", /* Involuntary context switches (atomic) */
447
- "utime", /* User time used (PL) */
448
- "stime", /* System time used (PL) */
449
- NULL
450
- );
451
- }