proc-wait3 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data/CHANGES +61 -0
  2. data/README +53 -0
  3. data/doc/wait3.txt +154 -0
  4. data/extconf.rb +54 -0
  5. data/lib/proc/wait3.c +908 -0
  6. data/test/tc_wait3.rb +199 -0
  7. metadata +51 -0
data/CHANGES ADDED
@@ -0,0 +1,61 @@
1
+ == 1.5.1 - 13-Jul-2006
2
+ * Fixed bugs with improper values being set for some of the rlimit constants.
3
+ * Cleaned up a few warnings related to signed-ness for the RLIM_xxx constants.
4
+ * Now only sets the various rlimit constants if they're not already defined
5
+ (which Ruby now defines, as of 1.8.5).
6
+ * Some internal cleanup.
7
+ * Created a gemspec and added a gem to RubyForge.
8
+
9
+ == 1.5.0 - 12-Jun-2006
10
+ * Removed the '?' character from the various struct members, since Ruby
11
+ no longer (properly) handles them.
12
+ * Fixed a 64 bit bug related to rb_struct_define.
13
+ * Added some more tests.
14
+
15
+ == 1.4.3 - 28-Jun-2005
16
+ * Added more #ifdef checks for some of the process flags which, it turns
17
+ out, are not defined in earlier versions of Linux.
18
+
19
+ == 1.4.2 - 14-Jun-2005
20
+ * Fixed a syntax error that could cause the build to fail.
21
+ * Removed some (but not all) possible warnings from gcc -Wall.
22
+
23
+ == 1.4.1 - 13-Jun-2005
24
+ * Added support for the Linux 2.6.9+ kernel (by adding more preprocessor
25
+ constant checks, which may help with other platforms as well).
26
+ * Moved project to RubyForge.
27
+ * Modified test suite - some tests now skipped on Linux.
28
+ * Removed the wait3.rd file.
29
+ * Minor fix for the test_waitid.rb sample program.
30
+
31
+ == 1.4.0 - 16-Feb-2005
32
+ * Added the getrusage method.
33
+ * Added test cases and documentation for getrusage.
34
+ * Renamed a couple test files in the examples directory.
35
+
36
+ == 1.3.0 - 14-Feb-2005
37
+ * Added the pause and sigsend methods.
38
+ * I had to modify the process type constants to include the "P_", because
39
+ Ruby already has Process::GID and Process::UID defined. That makes this
40
+ release incompatible with previous versions.
41
+ * Updated tests and documentation.
42
+
43
+ == 1.2.0 - 7-Feb-2005
44
+ * Added the Proc.waitid method (for those platforms that support it).
45
+ * Made the wait3.c file more rdoc friendly.
46
+ * Added a test_waitid.rb file in the examples directory.
47
+
48
+ == 1.1.1 - 10-Jan-2005
49
+ * Eliminated some (harmless) warnings that cropped up in 1.8.2
50
+ * Moved the "examples" directory to the toplevel directory.
51
+ * Made docs slightly more rdoc friendly
52
+
53
+ == 1.1.0 - 14-Sep-2004
54
+ * Modified setup and source to handle the possibility that wait3() might
55
+ be defined while wait4() is not (e.g. HPUX).
56
+ * Modified the test scripts in the examples directory to play nice on HPUX
57
+ and Darwin.
58
+ * Added this file (oops).
59
+
60
+ == 1.0.0 - 13-Sep-2004
61
+ - Initial release
data/README ADDED
@@ -0,0 +1,53 @@
1
+ == Description
2
+ Adds the wait3, wait4, waitid, pause, sigsend, and getrusage methods to
3
+ the Process module.
4
+
5
+ == Installation
6
+ ruby extconf.rb
7
+ make
8
+ ruby test/tc_wait3.rb (optional)
9
+ make site-install
10
+
11
+ == Tested Platforms
12
+ * Solaris 9
13
+ * Solaris 10
14
+ * Linux 2.4.7 (RedHat)
15
+ * Linux 2.6.11 (Suse)
16
+ * Linux 2.6.8.1 (Mandriva)
17
+ * FreeBSD 4.9
18
+
19
+ == Warnings
20
+ Linux users who compile with gcc -Wall will notice a few warnings. These
21
+ are harmless (and unavoidable atm).
22
+
23
+ == Applying the mkmf.diff patch
24
+ In order to install this package, you will need to patch your mkmf.rb file with
25
+ the provided diff file. This is necessary because I need a way to determine
26
+ if certain idtype_t enum (const) values are defined. The patch adds a
27
+ "have_const" method (and does nothing else). See ruby-core:4422 and related
28
+ threads for more information. Also available as ruby-Patches-1486 on the
29
+ RubyForge project page for Ruby.
30
+
31
+ To apply the patch, simply run "ruby patch.rb". Note that you may need to be
32
+ root to apply the patch.
33
+
34
+ == Integration with Ruby's process.c
35
+ I considered simply providing a patch to the core process.c file, but I
36
+ decided against it for two reasons. First, I wanted to get something
37
+ out more quickly rather than waiting for approval from the core developers who,
38
+ based on an earlier post, seem somewhat gun-shy about integrating support
39
+ for wait3() and wait4() based, I think, on portability concerns.
40
+
41
+ Second, and more importantly, I don't like the cProcStatus class. The
42
+ extra inspection code seems like an awful lot of work for very little gain.
43
+ The overloaded methods are also overkill, and do nothing but save me the
44
+ trouble of typing the word "status", since all they're for is comparing or
45
+ operating on the status attribute.
46
+
47
+ That being said, I would be willing to write a patch for process.c if there's
48
+ enough support for it. If you'd like to see that, please log a comment and/or
49
+ feature request on the project page at
50
+ http://www.rubyforge.org/projects/shards.
51
+
52
+ == Additional Documentation
53
+ Please see the doc/wait3.txt file for detailed documentation.
@@ -0,0 +1,154 @@
1
+ == Description
2
+ Adds the wait3, wait4, waitid, pause, sigsend, and getrusage methods to
3
+ the Process module.
4
+
5
+ == Synopsis
6
+ require "proc/wait3"
7
+
8
+ pid = fork{ sleep 1; exit 2 }
9
+ p Time.now
10
+ Process.wait3
11
+ p $?
12
+
13
+ == Module Methods
14
+ Proc.pause(signals=nil)
15
+ Pauses the current process. If the process receives any of the signals
16
+ you pass as arguments it will return from the pause and continue with
17
+ the execution of your code. Otherwise, it will exit.
18
+
19
+ Note that you must leave out the 'SIG' prefix for the signal name, e.g.
20
+ use 'INT', not 'SIGINT'.
21
+
22
+ Returns the result of the pause() function, which should always be -1.
23
+
24
+ Process.sigsend(idtype, id, signal=0)
25
+ Sends a signal of type "idtype" to a process or process group "id". This
26
+ is more versatile method of sending signals to processes than Process.kill.
27
+
28
+ For a list of valid idtype values, see the "Process type constants" below.
29
+ Not supported on all platforms.
30
+
31
+ Proc.wait3(flags=0)
32
+ Delays its caller until a signal is received or one of its child processes
33
+ terminates or stops due to tracing.
34
+
35
+ The return value is a ProcStat structure. The special global $? is also
36
+ set. Raises a SystemError if there are no child processes.
37
+
38
+ Proc.wait4(pid, flags=0)
39
+ Waits for the given child process to exit. Returns a ProcStat structure.
40
+ Also sets the $? special global variable.
41
+
42
+ Proc.waitid(id_type, id_num=nil, options=nil)
43
+ Suspends the calling process until one of its children changes state,
44
+ returning immediately if a child process changed state prior to the call.
45
+ The state of a child process will change if it terminates, stops because
46
+ of a signal, becomes trapped or reaches a breakpoint.
47
+
48
+ The id_num corresponds to a pid or pgid, depending on the value of id_type,
49
+ which may be Process::P_PID, Process::P_PGID or Process::P_ALL. If
50
+ Process::P_ALL, then the id_num is ignored.
51
+
52
+ The options argument is used to specify which state changes are to be
53
+ waited for. It is constructed from the bitwise-OR of one or more of the
54
+ following constants:
55
+
56
+ Process::WCONTINUED
57
+ Process::WEXITED
58
+ Process::WNOHANG
59
+ Process::WNOWAIT
60
+ Process::WSTOPPED
61
+ Process::WTRAPPED (not supported on all platforms)
62
+
63
+ If Process::WNOHANG is set as an option, this method will return
64
+ immediately, whether or not a child has changed state.
65
+
66
+ Calling this method with an +id_type+ of Process::P_ALL and the options set
67
+ to 'Process::EXITED | Process::WTRAPPED' is equivalent to calling
68
+ Process.wait.
69
+
70
+ Returns a Proc::SigInfo struct and sets $?.
71
+
72
+ Not supported on all platforms.
73
+
74
+ == Constants
75
+
76
+ === Process type constants
77
+ ==== All platforms
78
+ Process::P_ALL
79
+ All non-system process.
80
+
81
+ Process::P_PID
82
+ A standard process id.
83
+
84
+ Process::P_PGID
85
+ Any non-system process group id.
86
+
87
+ ==== Not supported on all platforms
88
+ Process::P_CID
89
+ A scheduler process id.
90
+
91
+ Process::P_GID
92
+ Any non-system effective process group id.
93
+
94
+ Process::P_PROJID
95
+ A project process id. Solaris 8 or later only.
96
+
97
+ Process::P_SID
98
+ A session process id.
99
+
100
+ Process::P_TASKID
101
+ A task process id. Solaris 8 or later only.
102
+
103
+ Process::P_UID
104
+ Any non-system effective process user id.
105
+
106
+ === The following additional constants are defined if the waitid function is
107
+ supported on your system.
108
+
109
+ Process::WCONTINUED
110
+ Return the status for any child that was stopped and has been continued.
111
+
112
+ Process::WEXITED
113
+ Wait for process(es) to exit.
114
+
115
+ Process::WNOWAIT
116
+ Keep the process in a waitable state.
117
+
118
+ Process::WSTOPPED
119
+ Wait for and return the process status of any child that has stopped upon
120
+ receipt of a signal.
121
+
122
+ Process::WTRAPPED
123
+ Wait for traced process(es) to become trapped or reach a breakpoint.
124
+
125
+ Not supported on all platforms.
126
+
127
+ == Notes
128
+ The wait3 and wait4 methods are similar to the wait2() and waitpid2()
129
+ methods, except that they return much more information via the rusage
130
+ struct.
131
+
132
+ == Known Bugs
133
+ None that I'm aware of. Please log any bugs on the SourceForge project
134
+ page at http://ruby-miscutils.sf.net.
135
+
136
+ == License
137
+ Ruby's
138
+
139
+ == Copyright
140
+ (C) 2003-2006 Daniel J. Berger
141
+ All Rights Reserved.
142
+
143
+ == Warranty
144
+ This package is provided "as is" and without any express or
145
+ implied warranties, including, without limitation, the implied
146
+ warranties of merchantability and fitness for a particular purpose.
147
+
148
+ == Author
149
+ Daniel J. Berger
150
+ djberg96 at gmail dot com
151
+ imperator on IRC (irc.freenode.net)
152
+
153
+ == See also
154
+ wait3, wait4, waitid, pause, sigsend
@@ -0,0 +1,54 @@
1
+ ########################################################
2
+ # Use the mkmf.rb file that I provide, so I can use the
3
+ # have_enum_member method
4
+ ########################################################
5
+ require "mkmf"
6
+ require "ftools"
7
+
8
+ File.copy("lib/proc/wait3.c",".")
9
+
10
+ have_header("wait.h")
11
+
12
+ # wait3 is mandatory.
13
+ unless have_func("wait3")
14
+ STDERR.puts "wait3() function not found"
15
+ exit
16
+ end
17
+
18
+ # wait4 and waitid are optional (HPUX, et al)
19
+ have_func("wait4")
20
+ have_func("waitid")
21
+ have_func("sigsend")
22
+ have_func("getrusage")
23
+
24
+ have_struct_member("struct siginfo", "si_trapno", "signal.h")
25
+ have_struct_member("struct siginfo", "si_pc", "signal.h")
26
+ have_struct_member("struct siginfo", "si_sysarg", "signal.h")
27
+ have_struct_member("struct siginfo", "si_mstate", "signal.h")
28
+ have_struct_member("struct siginfo", "si_faddr", "signal.h")
29
+ have_struct_member("struct siginfo", "si_syscall", "signal.h")
30
+ have_struct_member("struct siginfo", "si_nsysarg", "signal.h")
31
+ have_struct_member("struct siginfo", "si_fault", "signal.h")
32
+ have_struct_member("struct siginfo", "si_tstamp", "signal.h")
33
+
34
+ begin
35
+ have_const("P_CID", "signal.h")
36
+ have_const("P_GID", "signal.h")
37
+ have_const("P_MYID", "signal.h")
38
+ have_const("P_SID", "signal.h")
39
+ have_const("P_UID", "signal.h")
40
+
41
+ # These are only supported by Solaris 8 and later afaik
42
+ have_const("P_PROJID", "signal.h")
43
+ have_const("P_TASKID", "signal.h")
44
+ rescue NoMethodError
45
+ STDERR.puts
46
+ STDERR.puts "STOP!"
47
+ STDERR.puts
48
+ STDERR.puts "Please run the patch.rb program and try again"
49
+ STDERR.puts "See the README file for more details"
50
+ STDERR.puts "Makefile NOT created"
51
+ exit
52
+ end
53
+
54
+ create_makefile("proc/wait3")
@@ -0,0 +1,908 @@
1
+ #include <ruby.h>
2
+ #include <string.h>
3
+ #include <unistd.h>
4
+
5
+ #ifdef HAVE_WAIT_H
6
+ #include <wait.h>
7
+ #else
8
+ #include <sys/resource.h>
9
+ #include <sys/wait.h>
10
+ #endif
11
+
12
+ #ifdef HAVE_WAITID
13
+ #include <signal.h>
14
+ #endif
15
+
16
+ #ifndef SIG2STR_MAX
17
+ #define SIG2STR_MAX 32
18
+ #endif
19
+
20
+ VALUE v_last_status;
21
+ VALUE v_procstat_struct, v_siginfo_struct, v_usage_struct;
22
+
23
+ static void sigproc(int signum);
24
+
25
+ /*
26
+ * Returns true if this process is stopped. This is only returned
27
+ * returned if the corresponding wait() call had the WUNTRACED flag
28
+ * set.
29
+ */
30
+ static VALUE pst_wifstopped(int status)
31
+ {
32
+ if(WIFSTOPPED(status))
33
+ return Qtrue;
34
+ else
35
+ return Qfalse;
36
+ }
37
+
38
+ /*
39
+ * Returns true if _stat_ terminated because of an uncaught signal.
40
+ */
41
+ static VALUE pst_wifsignaled(int status)
42
+ {
43
+ if (WIFSIGNALED(status))
44
+ return Qtrue;
45
+ else
46
+ return Qfalse;
47
+ }
48
+
49
+ /*
50
+ * Returns true if _stat_ exited normally (for example using an exit()
51
+ * call or finishing the program).
52
+ */
53
+ static VALUE pst_wifexited(int status)
54
+ {
55
+ if (WIFEXITED(status))
56
+ return Qtrue;
57
+ else
58
+ return Qfalse;
59
+ }
60
+
61
+ /*
62
+ * Returns true if _stat_ is successful, false otherwise.
63
+ * Returns nil if exited? is not true.
64
+ */
65
+ static VALUE pst_success_p(int status)
66
+ {
67
+ if (!WIFEXITED(status))
68
+ return Qnil;
69
+ return WEXITSTATUS(status) == EXIT_SUCCESS ? Qtrue : Qfalse;
70
+ }
71
+
72
+ /*
73
+ * Returns true if _stat_ generated a coredump when it terminated. Not
74
+ * available on all platforms.
75
+ */
76
+ static VALUE pst_wcoredump(int status)
77
+ {
78
+ #ifdef WCOREDUMP
79
+ if (WCOREDUMP(status))
80
+ return Qtrue;
81
+ else
82
+ return Qfalse;
83
+ #else
84
+ return Qfalse;
85
+ #endif
86
+ }
87
+
88
+ /*
89
+ * Returns the least significant eight bits of the return code of
90
+ * _stat_. Only available if exited? is true.
91
+ */
92
+ static VALUE pst_wexitstatus(int status)
93
+ {
94
+ if (WIFEXITED(status))
95
+ return INT2NUM(WEXITSTATUS(status));
96
+ return Qnil;
97
+ }
98
+
99
+ /*
100
+ * Returns the number of the signal that caused _stat_ to terminate
101
+ * (or nil if self was not terminated by an uncaught signal).
102
+ */
103
+ static VALUE pst_wtermsig(int status)
104
+ {
105
+ if (WIFSIGNALED(status))
106
+ return INT2NUM(WTERMSIG(status));
107
+ return Qnil;
108
+ }
109
+
110
+ /*
111
+ * Returns the number of the signal that caused _stat_ to stop (or nil
112
+ * if self is not stopped).
113
+ */
114
+ static VALUE pst_wstopsig(int status)
115
+ {
116
+ if(WIFSTOPPED(status))
117
+ return INT2NUM(WSTOPSIG(status));
118
+ return Qnil;
119
+ }
120
+
121
+ /*
122
+ * call-seq:
123
+ * Proc.wait3(flags=nil)
124
+ *
125
+ * Delays its caller until a signal is received or one of its child processes
126
+ * terminates or stops due to tracing.
127
+ *
128
+ * The return value is a ProcStat structure. The special global $? is also
129
+ * set. Raises a SystemError if there are no child processes.
130
+ */
131
+ static VALUE proc_wait3(int argc, VALUE *argv, VALUE mod){
132
+ int status;
133
+ int flags = 0;
134
+ struct rusage r;
135
+ pid_t pid;
136
+ VALUE v_flags = Qnil;
137
+
138
+ rb_scan_args(argc,argv,"01",&v_flags);
139
+
140
+ if(Qnil != v_flags){
141
+ flags = NUM2INT(v_flags);
142
+ }
143
+
144
+ pid = wait3(&status, flags, &r);
145
+
146
+ if(pid < 0){
147
+ rb_sys_fail("wait3");
148
+ }
149
+ else if(pid > 0){
150
+ v_last_status = rb_struct_new(v_procstat_struct,
151
+ INT2FIX(pid),
152
+ INT2FIX(status),
153
+ INT2FIX(r.ru_utime.tv_sec + (r.ru_utime.tv_usec/1000.0)),
154
+ INT2FIX(r.ru_stime.tv_sec + (r.ru_stime.tv_usec/1000.0)),
155
+ INT2FIX(r.ru_maxrss),
156
+ INT2FIX(r.ru_ixrss),
157
+ INT2FIX(r.ru_idrss),
158
+ INT2FIX(r.ru_isrss),
159
+ INT2FIX(r.ru_minflt),
160
+ INT2FIX(r.ru_majflt),
161
+ INT2FIX(r.ru_nswap),
162
+ INT2FIX(r.ru_inblock),
163
+ INT2FIX(r.ru_oublock),
164
+ INT2FIX(r.ru_msgsnd),
165
+ INT2FIX(r.ru_msgrcv),
166
+ INT2FIX(r.ru_nsignals),
167
+ INT2FIX(r.ru_nvcsw),
168
+ INT2FIX(r.ru_nivcsw),
169
+ pst_wifstopped(status),
170
+ pst_wifsignaled(status),
171
+ pst_wifexited(status),
172
+ pst_success_p(status),
173
+ pst_wcoredump(status),
174
+ pst_wexitstatus(status),
175
+ pst_wtermsig(status),
176
+ pst_wstopsig(status)
177
+ );
178
+ return v_last_status;
179
+ }
180
+ else{
181
+ return Qnil;
182
+ }
183
+ }
184
+
185
+ #ifdef HAVE_WAIT4
186
+ /*
187
+ * call-seq:
188
+ * Proc.wait4(pid, flags=0)
189
+ *
190
+ * Waits for the given child process to exit. Returns a ProcStat structure.
191
+ * Also sets the $? special global variable.
192
+ *
193
+ * This method is not supported on all platforms.
194
+ *
195
+ * Some +flags+ are not supported on all platforms.
196
+ */
197
+ static VALUE proc_wait4(int argc, VALUE *argv, VALUE mod){
198
+ int status;
199
+ int flags = 0;
200
+ struct rusage r;
201
+ pid_t pid;
202
+ VALUE v_pid;
203
+ VALUE v_flags = Qnil;
204
+
205
+ rb_scan_args(argc, argv, "11", &v_pid, &v_flags);
206
+
207
+ pid = NUM2INT(v_pid);
208
+
209
+ if(RTEST(v_flags))
210
+ flags = NUM2INT(v_flags);
211
+
212
+ pid = wait4(pid, &status, flags, &r);
213
+
214
+ if(pid < 0){
215
+ rb_sys_fail("wait4");
216
+ }
217
+ else if(pid > 0){
218
+ v_last_status = rb_struct_new(v_procstat_struct,
219
+ INT2FIX(pid),
220
+ INT2FIX(status),
221
+ INT2FIX(r.ru_utime.tv_sec + (r.ru_utime.tv_usec/1000.0)),
222
+ INT2FIX(r.ru_stime.tv_sec + (r.ru_stime.tv_usec/1000.0)),
223
+ INT2FIX(r.ru_maxrss),
224
+ INT2FIX(r.ru_ixrss),
225
+ INT2FIX(r.ru_idrss),
226
+ INT2FIX(r.ru_isrss),
227
+ INT2FIX(r.ru_minflt),
228
+ INT2FIX(r.ru_majflt),
229
+ INT2FIX(r.ru_nswap),
230
+ INT2FIX(r.ru_inblock),
231
+ INT2FIX(r.ru_oublock),
232
+ INT2FIX(r.ru_msgsnd),
233
+ INT2FIX(r.ru_msgrcv),
234
+ INT2FIX(r.ru_nsignals),
235
+ INT2FIX(r.ru_nvcsw),
236
+ INT2FIX(r.ru_nivcsw),
237
+ pst_wifstopped(status),
238
+ pst_wifsignaled(status),
239
+ pst_wifexited(status),
240
+ pst_success_p(status),
241
+ pst_wcoredump(status),
242
+ pst_wexitstatus(status),
243
+ pst_wtermsig(status),
244
+ pst_wstopsig(status)
245
+ );
246
+ return v_last_status;
247
+ }
248
+ else{
249
+ return Qnil;
250
+ }
251
+ }
252
+ #endif
253
+
254
+ #ifdef HAVE_WAITID
255
+ /*
256
+ * call-seq:
257
+ * Proc.waitid(id_type, id_num=nil, options=nil)
258
+ *
259
+ * Suspends the calling process until one of its children changes state,
260
+ * returning immediately if a child process changed state prior to the call.
261
+ * The state of a child process will change if it terminates, stops because
262
+ * of a signal, becomes trapped or reaches a breakpoint.
263
+ *
264
+ * The +id_num+ argument corresponds to a pid or pgid, depending on the value
265
+ * of +id_type+, which may be Process::P_PID, Process::P_GID or Process::P_ALL.
266
+ * If Process::P_ALL, then +id_num+ is ignored.
267
+ *
268
+ * The options argument is used to specify which state changes are to be
269
+ * waited for. It is constructed from the bitwise-OR of one or more of the
270
+ * following constants:
271
+ *
272
+ * Process::WCONTINUED
273
+ * Process::WEXITED
274
+ * Process::WNOHANG
275
+ * Process::WNOWAIT
276
+ * Process::WSTOPPED
277
+ * Process::WTRAPPED
278
+ *
279
+ * Not all of these constants are supported on all platforms.
280
+ *
281
+ * If Process::WNOHANG is set as an option, this method will return
282
+ * immediately, whether or not a child has changed state.
283
+ *
284
+ * Calling this method with an +id_type+ of Process::P_ALL and the options set
285
+ * to 'Process::WEXITED | Process::WTRAPPED' is equivalent to calling
286
+ * Process.wait.
287
+ *
288
+ * Returns a Proc::SigInfo struct and sets $?.
289
+ *
290
+ * Not supported on all platforms.
291
+ */
292
+ static VALUE proc_waitid(int argc, VALUE* argv, VALUE mod){
293
+ VALUE v_type, v_id, v_options;
294
+ siginfo_t infop;
295
+ idtype_t idtype;
296
+ id_t id = 0;
297
+ int options = 0;
298
+
299
+ rb_scan_args(argc, argv, "12", &v_type, &v_id, &v_options);
300
+
301
+ idtype = NUM2INT(v_type);
302
+
303
+ if(RTEST(v_id))
304
+ id = NUM2INT(v_id);
305
+
306
+ if(RTEST(v_options))
307
+ options = NUM2INT(v_options);
308
+
309
+ /* The Linux man page for waitid() says to zero out the pid field and check
310
+ * its value after the call to waitid() to detect if there were children in
311
+ * a waitable state or not (which we do later, below). Other platforms
312
+ * simply check the infop.si_signo struct member against SI_NOINFO.
313
+ */
314
+ #ifndef SI_NOINFO
315
+ infop.si_pid = 0;
316
+ #endif
317
+
318
+ if(waitid(idtype, id, &infop, options) == -1)
319
+ rb_sys_fail("waitid");
320
+
321
+ /* If the si_code struct member returns SI_NOINFO, or the si_pid member
322
+ * is still set to 0 after the call to waitid(), then only the si_signo
323
+ * member of the struct is meaningful. In that case, we'll set all other
324
+ * members to nil. Even if this condition doesn't arise, many of the
325
+ * SigInfo struct members may still be nil, depending on the value of
326
+ * si_signo.
327
+ *
328
+ * See Rich Teer's "Solaris Systems Programming", p 755 ff.
329
+ */
330
+
331
+ #ifdef SI_NOINFO
332
+ if(infop.si_code == SI_NOINFO){
333
+ #else
334
+ if(infop.si_pid == 0){
335
+ #endif
336
+ v_last_status = rb_struct_new(v_siginfo_struct,
337
+ INT2FIX(infop.si_signo),
338
+ INT2FIX(infop.si_errno),
339
+ Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, /* code, pid, uid, utime, status, stime */
340
+ #ifdef HAVE_ST_SI_TRAPNO
341
+ Qnil,
342
+ #endif
343
+ #ifdef HAVE_ST_SI_PC
344
+ Qnil,
345
+ #endif
346
+ Qnil, Qnil, /* fd, band */
347
+ #ifdef HAVE_ST_SI_FADDR
348
+ Qnil,
349
+ #endif
350
+ #ifdef HAVE_ST_SI_TSTAMP
351
+ Qnil,
352
+ #endif
353
+ #ifdef HAVE_ST_SI_SYSCALL
354
+ Qnil,
355
+ #endif
356
+ #ifdef HAVE_ST_SI_NSYSARG
357
+ Qnil,
358
+ #endif
359
+ #ifdef HAVE_ST_SI_FAULT
360
+ Qnil,
361
+ #endif
362
+ #ifdef HAVE_ST_SI_SYSARG
363
+ Qnil,
364
+ #endif
365
+ #ifdef HAVE_ST_SI_MSTATE
366
+ Qnil,
367
+ #endif
368
+ Qnil /* entity */
369
+ );
370
+ }
371
+ else{
372
+ VALUE rbUtime = Qnil, rbStatus = Qnil, rbStime = Qnil, rbFD = Qnil;
373
+ #ifdef HAVE_ST_SI_TRAPNO
374
+ VALUE rbTrapno = Qnil;
375
+ #endif
376
+ #ifdef HAVE_ST_SI_PC
377
+ VALUE rbPC = Qnil;
378
+ #endif
379
+ #ifdef HAVE_ST_SI_FADDR
380
+ VALUE rbFaddr = Qnil;
381
+ #endif
382
+ #ifdef HAVE_ST_SI_TSTAMP
383
+ VALUE rbTime = Qnil;
384
+ #endif
385
+ #ifdef HAVE_ST_SI_SYSCALL
386
+ VALUE rbSyscall = Qnil;
387
+ #endif
388
+ #ifdef HAVE_ST_SI_NSYSARG
389
+ VALUE rbNSysarg = Qnil;
390
+ #endif
391
+ #ifdef HAVE_ST_SI_FAULT
392
+ VALUE rbFault = Qnil;
393
+ #endif
394
+ #ifdef HAVE_ST_SI_SYSARG
395
+ VALUE rbSysarg = Qnil;
396
+ #endif
397
+ #ifdef HAVE_ST_SI_MSTATE
398
+ VALUE rbMState = Qnil;
399
+ #endif
400
+ VALUE rbBand = Qnil, rbEntity = Qnil;
401
+ int sig = infop.si_signo;
402
+ int code = infop.si_code;
403
+
404
+ /* If Process.waitid returns because a child process was found that
405
+ * satisfies the conditions indicated by +id_type+ and +options+, then
406
+ * the si_signo struct member will always be SIGCHLD.
407
+ */
408
+ if(sig == SIGCHLD){
409
+ rbUtime = ULL2NUM(infop.si_utime);
410
+ rbStatus = ULL2NUM(infop.si_status);
411
+ rbStime = ULL2NUM(infop.si_stime);
412
+ }
413
+
414
+ if(sig == SIGBUS || sig == SIGFPE || sig == SIGILL || sig == SIGSEGV ||
415
+ sig == SIGTRAP)
416
+ {
417
+ #ifdef HAVE_ST_SI_TRAPNO
418
+ rbTrapno = INT2FIX(infop.si_trapno);
419
+ #endif
420
+ #ifdef HAVE_ST_SI_PC
421
+ rbPC = INT2FIX(infop.si_pc);
422
+ #endif
423
+ }
424
+
425
+ if(sig == SIGXFSZ){
426
+ rbFD = INT2FIX(infop.si_fd);
427
+ if(code == POLL_IN || code == POLL_OUT || code == POLL_MSG){
428
+ rbBand = LONG2FIX(infop.si_band);
429
+ }
430
+ }
431
+
432
+ if(sig == SIGPROF){
433
+ int i = 0;
434
+ #ifdef HAVE_ST_SI_SYSARG
435
+ int ssize = sizeof(infop.si_sysarg) / sizeof(infop.si_sysarg[0]);
436
+ rbSysarg = rb_ary_new();
437
+
438
+ for(i = 0; i < ssize; i++)
439
+ rb_ary_push(rbSysarg, LONG2FIX(infop.si_sysarg[i]));
440
+ #endif
441
+ #ifdef HAVE_ST_SI_MSTATE
442
+ int msize = sizeof(infop.si_mstate) / sizeof(infop.si_mstate[0]);
443
+ rbMState = rb_ary_new();
444
+
445
+ for(i = 0; i < msize; i++)
446
+ rb_ary_push(rbMState, INT2FIX(infop.si_mstate[i]));
447
+ #endif
448
+ #ifdef HAVE_ST_SI_FADDR
449
+ rbFaddr = INT2FIX(infop.si_faddr);
450
+ #endif
451
+ #ifdef HAVE_ST_SI_SYSCALL
452
+ rbSyscall = INT2FIX(infop.si_syscall);
453
+ #endif
454
+ #ifdef HAVE_ST_SI_NSYSARG
455
+ rbNSysarg = INT2FIX(infop.si_nsysarg);
456
+ #endif
457
+ #ifdef HAVE_ST_SI_FAULT
458
+ rbFault = INT2FIX(infop.si_fault);
459
+ #endif
460
+ #ifdef HAVE_ST_SI_TSTAMP
461
+ rbTime = rb_time_new(infop.si_tstamp.tv_sec,infop.si_tstamp.tv_nsec);
462
+ #endif
463
+ }
464
+
465
+ #ifdef SIGXRES
466
+ if(sig == SIGXRES){
467
+ rbEntity = INT2FIX(infop.si_entity);
468
+ }
469
+ #endif
470
+
471
+ v_last_status = rb_struct_new(v_siginfo_struct,
472
+ INT2FIX(infop.si_signo), /* Probably SIGCHLD */
473
+ INT2FIX(infop.si_errno), /* 0 means no error */
474
+ INT2FIX(infop.si_code), /* Should be anything but SI_NOINFO */
475
+ INT2FIX(infop.si_pid), /* Real PID that sent the signal */
476
+ INT2FIX(infop.si_uid), /* Real UID of process that sent signal */
477
+ rbUtime,
478
+ rbStatus,
479
+ rbStime,
480
+ #ifdef HAVE_ST_SI_TRAPNO
481
+ rbTrapno,
482
+ #endif
483
+ #ifdef HAVE_ST_SI_PC
484
+ rbPC,
485
+ #endif
486
+ rbFD,
487
+ rbBand,
488
+ #ifdef HAVE_ST_SI_FADDR
489
+ rbFaddr,
490
+ #endif
491
+ #ifdef HAVE_ST_SI_TSTAMP
492
+ rbTime,
493
+ #endif
494
+ #ifdef HAVE_ST_SI_SYSCALL
495
+ rbSyscall,
496
+ #endif
497
+ #ifdef HAVE_ST_SI_NSYSARG
498
+ rbNSysarg,
499
+ #endif
500
+ #ifdef HAVE_ST_SI_FAULT
501
+ rbFault,
502
+ #endif
503
+ #ifdef HAVE_ST_SI_SYSARG
504
+ rbSysarg,
505
+ #endif
506
+ #ifdef HAVE_ST_SI_MSTATE
507
+ rbMState,
508
+ #endif
509
+ rbEntity
510
+ );
511
+ }
512
+
513
+ return v_last_status;
514
+ }
515
+ #endif
516
+
517
+ /*
518
+ * call-seq:
519
+ * Process.pause(signals=nil)
520
+ *
521
+ * Pauses the current process. If the process receives any of the +signals+
522
+ * you pass as arguments it will return from the pause and continue with
523
+ * the execution of your code. Otherwise, it will exit.
524
+ *
525
+ * Note that you must leave out the 'SIG' prefix for the signal name, e.g.
526
+ * use 'INT', not 'SIGINT'.
527
+ *
528
+ * Returns the result of the pause() function, which should always be -1.
529
+ */
530
+ static VALUE proc_pause(int argc, VALUE* argv, VALUE mod){
531
+ VALUE v_signals;
532
+ int i, len;
533
+
534
+ rb_scan_args(argc, argv, "0*", &v_signals);
535
+
536
+ /* Iterate over each signal, calling sigset for each one */
537
+ len = RARRAY(v_signals)->len;
538
+ if(len > 0){
539
+ VALUE v_val;
540
+ char signame[SIG2STR_MAX];
541
+ unsigned int max = SIG2STR_MAX;
542
+ int signum;
543
+
544
+ for(i = 0; i < len; i++){
545
+ v_val = rb_ary_shift(v_signals);
546
+
547
+ if(strlcpy(signame, StringValuePtr(v_val), max) >= max)
548
+ rb_raise(rb_eArgError, "string too large");
549
+
550
+ if(str2sig(signame, &signum) != 0)
551
+ rb_sys_fail("pause");
552
+
553
+ sigset(signum, sigproc);
554
+ }
555
+ }
556
+
557
+ return INT2FIX(pause()); /* Should always be -1 */
558
+ }
559
+
560
+ /*
561
+ * This is just a placeholder proc to prevent the "pause" method from exiting
562
+ * the program if the appropriate signal is intercepted.
563
+ */
564
+ static void sigproc(int signum){ /* Do nothing */ }
565
+
566
+ #ifdef HAVE_SIGSEND
567
+ /*
568
+ * call-seq:
569
+ * Process.sigsend(idtype, id, signal=0)
570
+ *
571
+ * Sends a signal of type +idtype+ to a process or process group. This is
572
+ * more versatile method of sending signals to processes than Process.kill.
573
+ *
574
+ * The idtype * must be one of the following values:
575
+ *
576
+ * * Process::P_ALL
577
+ * All non-system processes. The +id+ is ignored.
578
+ *
579
+ * * Process::P_CID
580
+ * Any process whose scheduler class ID is equal to +id+.
581
+ *
582
+ * * Process::P_GID
583
+ * Any non-system process whose effective group ID is equal to +id+.
584
+ *
585
+ * * Process::P_PGID
586
+ * Any non-system process whose process group ID is equal to +id+.
587
+ *
588
+ * * Process::P_PID
589
+ * The process ID equal to +id+.
590
+ *
591
+ * * Process::P_PROJID
592
+ * All processes whose project ID id equal to +id+. Solaris 8 or later
593
+ * only.
594
+ *
595
+ * * Process::P_SID
596
+ * Any non-system process whose session ID is equal to +id+.
597
+ *
598
+ * * Process::P_TASKID
599
+ * All processes whose task ID is equal to +id+. Solaris 8 or later
600
+ * only.
601
+ *
602
+ * * Process::P_UID
603
+ * Any non-system process whose effective user ID is equal to +id+.
604
+ */
605
+ static VALUE proc_sigsend(int argc, VALUE* argv, VALUE mod){
606
+ VALUE v_type, v_pid, v_signal;
607
+ idtype_t idtype;
608
+ id_t id;
609
+ int sig = 0; /* 0 is our default signal (i.e. no signal) */
610
+
611
+ rb_scan_args(argc, argv, "21", &v_type, &v_pid, &v_signal);
612
+
613
+ idtype = NUM2INT(v_type);
614
+ id = NUM2INT(v_pid);
615
+
616
+ if(!NIL_P(v_signal)){
617
+ if(TYPE(v_signal) == T_FIXNUM){
618
+ sig = FIX2INT(v_signal);
619
+ }
620
+ else{
621
+ char signame[SIG2STR_MAX];
622
+ unsigned int max = SIG2STR_MAX;
623
+
624
+ if(strlcpy(signame, StringValuePtr(v_signal), max) >= max)
625
+ rb_raise(rb_eArgError, "string too large");
626
+
627
+ if(str2sig(signame,&sig) != 0)
628
+ rb_sys_fail("str2sig");
629
+ }
630
+ }
631
+
632
+ if(sigsend(idtype,id,sig) != 0)
633
+ rb_sys_fail("sigsend");
634
+
635
+ return Qnil;
636
+ }
637
+ #endif
638
+
639
+ #ifdef HAVE_GETRUSAGE
640
+ /*
641
+ * call-seq:
642
+ * Process.getrusage(children=false)
643
+ *
644
+ * Returns comprehensive process resource usage information in the form of a
645
+ * RUsage struct. By default, this will return information for the current
646
+ * process. If +children+ is set to true, it will return information for
647
+ * terminated and waited for children of the current process.
648
+ *
649
+ * The RUsage struct contains the following members:
650
+ *
651
+ * * utime - User time
652
+ * * stime - System time
653
+ * * maxrss - Maximum resident set size
654
+ * * intrss - Integral shared memory size
655
+ * * idrss - Integral unshared data size
656
+ * * isrss - Integral unshared statck size
657
+ * * minflt - Minor page faults
658
+ * * majflt - Major page faults
659
+ * * nswap - Number of swaps
660
+ * * inblock - Block input operations
661
+ * * oublock - Block output operations
662
+ * * msgsnd - Messages sent
663
+ * * msgrcv - Messages received
664
+ * * nsignals - Number of signals received
665
+ * * nvcsw - Voluntary context switches
666
+ * * nivcsw - Involuntary context switches
667
+ *
668
+ * Note that not all members contain meaningful values on all platforms.
669
+ */
670
+ static VALUE proc_getrusage(int argc, VALUE* argv, VALUE mod){
671
+ VALUE v_children = Qfalse;
672
+ struct rusage r;
673
+ int who = RUSAGE_SELF;
674
+
675
+ rb_scan_args(argc, argv, "01", &v_children);
676
+
677
+ if(Qtrue == v_children)
678
+ who = RUSAGE_CHILDREN;
679
+
680
+ if(getrusage(who,&r) == -1)
681
+ rb_sys_fail("getrusage");
682
+
683
+ return rb_struct_new(v_usage_struct,
684
+ LONG2FIX(r.ru_utime.tv_sec),
685
+ LONG2FIX(r.ru_stime.tv_sec),
686
+ LONG2FIX(r.ru_maxrss),
687
+ LONG2FIX(r.ru_ixrss),
688
+ LONG2FIX(r.ru_idrss),
689
+ LONG2FIX(r.ru_isrss),
690
+ LONG2FIX(r.ru_minflt),
691
+ LONG2FIX(r.ru_majflt),
692
+ LONG2FIX(r.ru_nswap),
693
+ LONG2FIX(r.ru_inblock),
694
+ LONG2FIX(r.ru_oublock),
695
+ LONG2FIX(r.ru_msgsnd),
696
+ LONG2FIX(r.ru_msgrcv),
697
+ LONG2FIX(r.ru_nsignals),
698
+ LONG2FIX(r.ru_nvcsw),
699
+ LONG2FIX(r.ru_nivcsw)
700
+ );
701
+ }
702
+ #endif
703
+
704
+ /*
705
+ * call-seq:
706
+ * Process.getrlimit(resource)
707
+ *
708
+ * Returns a two element array consisting of the hard and soft limit
709
+ * for the current process for the given +resource+. The array consists of
710
+ * either numeric values or the word "infinite".
711
+ */
712
+ static VALUE proc_getrlimit(VALUE mod, VALUE rbResource){
713
+ struct rlimit limits;
714
+ VALUE v_array = rb_ary_new();
715
+
716
+ rb_secure(2);
717
+
718
+ if(getrlimit(NUM2INT(rbResource), &limits) < 0)
719
+ rb_sys_fail("getrlimit");
720
+
721
+ if(limits.rlim_cur == RLIM_INFINITY)
722
+ rb_ary_push(v_array, rb_str_new2("infinity"));
723
+ else
724
+ rb_ary_push(v_array, INT2FIX(limits.rlim_cur));
725
+
726
+ if(limits.rlim_max == RLIM_INFINITY)
727
+ rb_ary_push(v_array, rb_str_new2("infinity"));
728
+ else
729
+ rb_ary_push(v_array, INT2FIX(limits.rlim_max));
730
+
731
+ return v_array;
732
+ }
733
+
734
+ /*
735
+ * call-seq;
736
+ * Process.setrlimit(resource, current, max)
737
+ *
738
+ * Sets the current (soft) and max (hard) limit for the given +resource+ for
739
+ * the current process.
740
+ */
741
+ static VALUE proc_setrlimit(VALUE mod, VALUE rbRes, VALUE rbCur, VALUE rbMax){
742
+ struct rlimit limits;
743
+
744
+ rb_secure(2);
745
+
746
+ limits.rlim_cur = NUM2INT(rbCur);
747
+ limits.rlim_max = NUM2INT(rbMax);
748
+
749
+ if(setrlimit(NUM2INT(rbRes), &limits) == -1)
750
+ rb_sys_fail("setrlimit");
751
+
752
+ return Qnil;
753
+ }
754
+
755
+ /*
756
+ * Adds the wait3, wait4, waitid, pause, sigsend, and getrusage methods to the
757
+ * Process module.
758
+ */
759
+ void Init_wait3()
760
+ {
761
+ v_procstat_struct =
762
+ rb_struct_define("ProcStat","pid","status","utime","stime","maxrss",
763
+ "ixrss", "idrss", "isrss", "minflt","majflt","nswap","inblock",
764
+ "oublock","msgsnd", "msgrcv","nsignals","nvcsw","nivcsw","stopped",
765
+ "signaled","exited","success","coredump","exitstatus","termsig",
766
+ "stopsig",NULL
767
+ );
768
+
769
+ rb_define_module_function(rb_mProcess, "wait3", proc_wait3, -1);
770
+ rb_define_module_function(rb_mProcess, "pause", proc_pause, -1);
771
+ rb_define_module_function(rb_mProcess, "getrlimit", proc_getrlimit, 1);
772
+ rb_define_module_function(rb_mProcess, "setrlimit", proc_setrlimit, 3);
773
+
774
+ #ifdef HAVE_SIGSEND
775
+ rb_define_module_function(rb_mProcess, "sigsend", proc_sigsend, -1);
776
+ #endif
777
+
778
+ #ifdef HAVE_WAIT4
779
+ rb_define_module_function(rb_mProcess, "wait4", proc_wait4, -1);
780
+ #endif
781
+
782
+ #ifdef HAVE_GETRUSAGE
783
+ v_usage_struct =
784
+ rb_struct_define("RUsage","utime","stime","maxrss","ixrss","idrss",
785
+ "isrss","minflt","majflt","nswap","inblock","oublock","msgsnd",
786
+ "msgrcv","nsignals","nvcsw","nivcsw",NULL
787
+ );
788
+
789
+ rb_define_module_function(rb_mProcess, "getrusage", proc_getrusage, -1);
790
+ #endif
791
+
792
+ #ifdef HAVE_WAITID
793
+ v_siginfo_struct =
794
+ rb_struct_define("SigInfo","signo","errno","code","pid","uid",
795
+ "utime","status","stime"
796
+ #ifdef HAVE_ST_SI_TRAPNO
797
+ ,"trapno"
798
+ #endif
799
+ #ifdef HAVE_ST_SI_PC
800
+ ,"pc"
801
+ #endif
802
+ ,"fd","band"
803
+ #ifdef HAVE_ST_SI_FADDR
804
+ ,"faddr"
805
+ #endif
806
+ #ifdef HAVE_ST_SI_TSTAMP
807
+ ,"tstamp"
808
+ #endif
809
+ #ifdef HAVE_ST_SI_SYSCALL
810
+ ,"syscall"
811
+ #endif
812
+ #ifdef HAVE_ST_SI_NSYSARG
813
+ ,"nsysarg"
814
+ #endif
815
+ #ifdef HAVE_ST_SI_FAULT
816
+ ,"fault"
817
+ #endif
818
+ #ifdef HAVE_ST_SI_SYSARG
819
+ ,"sysarg"
820
+ #endif
821
+ #ifdef HAVE_ST_SI_MSTATE
822
+ ,"mstate"
823
+ #endif
824
+ ,"entity", NULL
825
+ );
826
+
827
+ rb_define_module_function(rb_mProcess, "waitid", proc_waitid, -1);
828
+
829
+ #ifdef WCONTINUED
830
+ rb_define_const(rb_mProcess, "WCONTINUED", INT2FIX(WCONTINUED));
831
+ #endif
832
+
833
+ #ifdef WEXITED
834
+ rb_define_const(rb_mProcess, "WEXITED", INT2FIX(WEXITED));
835
+ #endif
836
+
837
+ #ifdef WNOWAIT
838
+ rb_define_const(rb_mProcess, "WNOWAIT", INT2FIX(WNOWAIT));
839
+ #endif
840
+
841
+ #ifdef WSTOPPED
842
+ rb_define_const(rb_mProcess, "WSTOPPED", INT2FIX(WSTOPPED));
843
+ #endif
844
+
845
+ #ifdef WTRAPPED
846
+ rb_define_const(rb_mProcess, "WTRAPPED", INT2FIX(WTRAPPED));
847
+ #endif
848
+ #endif
849
+
850
+ /* Because core Ruby already defines a Process::GID and Process::UID,
851
+ * I am forced to keep the leading 'P_' for these constants.
852
+ */
853
+ rb_define_const(rb_mProcess, "P_ALL", INT2FIX(P_ALL));
854
+ rb_define_const(rb_mProcess, "P_PGID", INT2FIX(P_PGID));
855
+ rb_define_const(rb_mProcess, "P_PID", INT2FIX(P_PID));
856
+
857
+ #ifdef HAVE_CONST_P_CID
858
+ rb_define_const(rb_mProcess, "P_CID", INT2FIX(P_CID));
859
+ #endif
860
+
861
+ #ifdef HAVE_CONST_P_GID
862
+ rb_define_const(rb_mProcess, "P_GID", INT2FIX(P_GID));
863
+ #endif
864
+
865
+ #ifdef HAVE_CONST_P_MYID
866
+ rb_define_const(rb_mProcess, "P_MYID", INT2FIX(P_MYID));
867
+ #endif
868
+
869
+ #ifdef HAVE_CONST_P_SID
870
+ rb_define_const(rb_mProcess, "P_SID", INT2FIX(P_SID));
871
+ #endif
872
+
873
+ #ifdef HAVE_CONST_P_UID
874
+ rb_define_const(rb_mProcess, "P_UID", INT2FIX(P_UID));
875
+ #endif
876
+
877
+ #ifdef HAVE_CONST_P_TASKID
878
+ rb_define_const(rb_mProcess, "P_TASKID", INT2FIX(P_TASKID));
879
+ #endif
880
+
881
+ #ifdef HAVE_CONST_P_PROJID
882
+ rb_define_const(rb_mProcess, "P_PROJID", INT2FIX(P_PROJID));
883
+ #endif
884
+
885
+ /* Constants for getrlimit, setrlimit. It appears that these are defined
886
+ * by Ruby as of 1.8.5. Assume that if RLIMIT_AS is defined, all the
887
+ * standard rlimit constants are defined.
888
+ */
889
+ #ifndef RLIMIT_AS
890
+ rb_define_const(rb_mProcess, "RLIMIT_AS", INT2FIX(RLIMIT_AS));
891
+ rb_define_const(rb_mProcess, "RLIMIT_CORE", INT2FIX(RLIMIT_CORE));
892
+ rb_define_const(rb_mProcess, "RLIMIT_CPU", INT2FIX(RLIMIT_CPU));
893
+ rb_define_const(rb_mProcess, "RLIMIT_DATA", INT2FIX(RLIMIT_DATA));
894
+ rb_define_const(rb_mProcess, "RLIMIT_FSIZE", INT2FIX(RLIMIT_FSIZE));
895
+ rb_define_const(rb_mProcess, "RLIMIT_NOFILE", INT2FIX(RLIMIT_NOFILE));
896
+ rb_define_const(rb_mProcess, "RLIMIT_STACK", INT2FIX(RLIMIT_STACK));
897
+ rb_define_const(rb_mProcess, "RLIM_INFINITY", UINT2NUM(RLIM_INFINITY));
898
+ rb_define_const(rb_mProcess, "RLIM_SAVED_MAX", UINT2NUM(RLIM_SAVED_MAX));
899
+ rb_define_const(rb_mProcess, "RLIM_SAVED_CUR", UINT2NUM(RLIM_SAVED_CUR));
900
+ #endif
901
+
902
+ #ifdef RLIMIT_VMEM
903
+ rb_define_const(rb_mProcess, "RLIMIT_VMEM", INT2FIX(RLIMIT_VMEM));
904
+ #endif
905
+
906
+ /* Define this last in our Init_wait3 function */
907
+ rb_define_readonly_variable("$?", &v_last_status);
908
+ }