posix-spawn 0.3.3 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +6 -6
- data/ext/posix-spawn.c +51 -9
- data/lib/posix/spawn.rb +8 -2
- data/lib/posix/spawn/child.rb +19 -13
- data/lib/posix/spawn/version.rb +1 -1
- data/test/test_spawn.rb +32 -1
- metadata +18 -2
data/README.md
CHANGED
@@ -179,6 +179,12 @@ These `Process::spawn` arguments are currently supported to any of
|
|
179
179
|
clearing environment variables:
|
180
180
|
:unsetenv_others => true : clear environment variables except specified by env
|
181
181
|
:unsetenv_others => false : don't clear (default)
|
182
|
+
current directory:
|
183
|
+
:chdir => str
|
184
|
+
process group:
|
185
|
+
:pgroup => true or 0 : make a new process group
|
186
|
+
:pgroup => pgid : join to specified process group
|
187
|
+
:pgroup => nil : don't change the process group (default)
|
182
188
|
redirection:
|
183
189
|
key:
|
184
190
|
FD : single file descriptor in child process
|
@@ -196,16 +202,10 @@ These `Process::spawn` arguments are currently supported to any of
|
|
196
202
|
:err : the file descriptor 2 which is the standard error
|
197
203
|
integer : the file descriptor of specified the integer
|
198
204
|
io : the file descriptor specified as io.fileno
|
199
|
-
current directory:
|
200
|
-
:chdir => str
|
201
205
|
|
202
206
|
These options are currently NOT supported:
|
203
207
|
|
204
208
|
options: hash
|
205
|
-
process group:
|
206
|
-
:pgroup => true or 0 : make a new process group
|
207
|
-
:pgroup => pgid : join to specified process group
|
208
|
-
:pgroup => nil : don't change the process group (default)
|
209
209
|
resource limit: resourcename is core, cpu, data, etc. See Process.setrlimit.
|
210
210
|
:rlimit_resourcename => limit
|
211
211
|
:rlimit_resourcename => [cur_limit, max_limit]
|
data/ext/posix-spawn.c
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
|
6
6
|
#include <errno.h>
|
7
7
|
#include <fcntl.h>
|
8
|
+
#include <signal.h>
|
8
9
|
#include <spawn.h>
|
9
10
|
#include <stdio.h>
|
10
11
|
#include <string.h>
|
@@ -14,13 +15,8 @@
|
|
14
15
|
|
15
16
|
#ifdef RUBY_VM
|
16
17
|
#include <ruby/st.h>
|
17
|
-
extern void rb_enable_interrupt(void);
|
18
|
-
extern void rb_disable_interrupt(void);
|
19
18
|
#else
|
20
|
-
#include <node.h>
|
21
19
|
#include <st.h>
|
22
|
-
#define rb_enable_interrupt()
|
23
|
-
#define rb_disable_interrupt()
|
24
20
|
#endif
|
25
21
|
|
26
22
|
#ifndef RARRAY_LEN
|
@@ -209,6 +205,42 @@ posixspawn_file_actions_init(posix_spawn_file_actions_t *fops, VALUE options)
|
|
209
205
|
rb_hash_foreach(options, posixspawn_file_actions_operations_iter, (VALUE)fops);
|
210
206
|
}
|
211
207
|
|
208
|
+
/*
|
209
|
+
* Initialize pgroup related flags in the posix_spawnattr struct based on the
|
210
|
+
* options Hash.
|
211
|
+
*
|
212
|
+
* :pgroup => 0 | true - spawned process is in a new process group with the
|
213
|
+
* same id as the new process's pid.
|
214
|
+
* :pgroup => pgid - spawned process is in a new process group with id
|
215
|
+
* pgid.
|
216
|
+
* :pgroup => nil - spawned process has the same pgid as the parent
|
217
|
+
* process (this is the default).
|
218
|
+
*
|
219
|
+
* The options Hash is modified in place with the :pgroup key being removed.
|
220
|
+
*/
|
221
|
+
static void
|
222
|
+
posixspawn_set_pgroup(VALUE options, posix_spawnattr_t *pattr, short *pflags)
|
223
|
+
{
|
224
|
+
VALUE pgroup_val;
|
225
|
+
pgroup_val = rb_hash_delete(options, ID2SYM(rb_intern("pgroup")));
|
226
|
+
|
227
|
+
switch (TYPE(pgroup_val)) {
|
228
|
+
case T_TRUE:
|
229
|
+
(*pflags) |= POSIX_SPAWN_SETPGROUP;
|
230
|
+
posix_spawnattr_setpgroup(pattr, 0);
|
231
|
+
break;
|
232
|
+
case T_FIXNUM:
|
233
|
+
(*pflags) |= POSIX_SPAWN_SETPGROUP;
|
234
|
+
posix_spawnattr_setpgroup(pattr, FIX2INT(pgroup_val));
|
235
|
+
break;
|
236
|
+
case T_NIL:
|
237
|
+
break;
|
238
|
+
default:
|
239
|
+
rb_raise(rb_eTypeError, ":pgroup option is invalid");
|
240
|
+
break;
|
241
|
+
}
|
242
|
+
}
|
243
|
+
|
212
244
|
static int
|
213
245
|
each_env_check_i(VALUE key, VALUE val, VALUE arg)
|
214
246
|
{
|
@@ -289,6 +321,8 @@ rb_posixspawn_pspawn(VALUE self, VALUE env, VALUE argv, VALUE options)
|
|
289
321
|
pid_t pid;
|
290
322
|
posix_spawn_file_actions_t fops;
|
291
323
|
posix_spawnattr_t attr;
|
324
|
+
sigset_t mask;
|
325
|
+
short flags = 0;
|
292
326
|
|
293
327
|
/* argv is a [[cmdname, argv0], argv1, argvN, ...] array. */
|
294
328
|
if (TYPE(argv) != T_ARRAY ||
|
@@ -347,15 +381,25 @@ rb_posixspawn_pspawn(VALUE self, VALUE env, VALUE argv, VALUE options)
|
|
347
381
|
}
|
348
382
|
|
349
383
|
posixspawn_file_actions_init(&fops, options);
|
350
|
-
|
351
384
|
posix_spawnattr_init(&attr);
|
385
|
+
|
386
|
+
/* child does not block any signals */
|
387
|
+
flags |= POSIX_SPAWN_SETSIGMASK;
|
388
|
+
sigemptyset(&mask);
|
389
|
+
posix_spawnattr_setsigmask(&attr, &mask);
|
390
|
+
|
352
391
|
#if defined(POSIX_SPAWN_USEVFORK) || defined(__linux__)
|
353
392
|
/* Force USEVFORK on linux. If this is undefined, it's probably because
|
354
393
|
* you forgot to define _GNU_SOURCE at the top of this file.
|
355
394
|
*/
|
356
|
-
|
395
|
+
flags |= POSIX_SPAWN_USEVFORK;
|
357
396
|
#endif
|
358
397
|
|
398
|
+
/* setup pgroup options */
|
399
|
+
posixspawn_set_pgroup(options, &attr, &flags);
|
400
|
+
|
401
|
+
posix_spawnattr_setflags(&attr, flags);
|
402
|
+
|
359
403
|
if (RTEST(dirname = rb_hash_delete(options, ID2SYM(rb_intern("chdir"))))) {
|
360
404
|
char *new_cwd = StringValuePtr(dirname);
|
361
405
|
cwd = getcwd(NULL, 0);
|
@@ -363,9 +407,7 @@ rb_posixspawn_pspawn(VALUE self, VALUE env, VALUE argv, VALUE options)
|
|
363
407
|
}
|
364
408
|
|
365
409
|
if (RHASH_SIZE(options) == 0) {
|
366
|
-
rb_enable_interrupt();
|
367
410
|
ret = posix_spawnp(&pid, file, &fops, &attr, cargv, envp ? envp : environ);
|
368
|
-
rb_disable_interrupt();
|
369
411
|
if (cwd) {
|
370
412
|
chdir(cwd);
|
371
413
|
free(cwd);
|
data/lib/posix/spawn.rb
CHANGED
@@ -159,8 +159,9 @@ module POSIX
|
|
159
159
|
# the POSIX::Spawn module documentation.
|
160
160
|
def fspawn(*args)
|
161
161
|
env, argv, options = extract_process_spawn_arguments(*args)
|
162
|
+
valid_options = [:chdir, :unsetenv_others, :pgroup]
|
162
163
|
|
163
|
-
if badopt = options.find{ |key,val| !fd?(key) && !
|
164
|
+
if badopt = options.find{ |key,val| !fd?(key) && !valid_options.include?(key) }
|
164
165
|
raise ArgumentError, "Invalid option: #{badopt[0].inspect}"
|
165
166
|
elsif !argv.is_a?(Array) || !argv[0].is_a?(Array) || argv[0].size != 2
|
166
167
|
raise ArgumentError, "Invalid command name"
|
@@ -169,7 +170,7 @@ module POSIX
|
|
169
170
|
fork do
|
170
171
|
begin
|
171
172
|
# handle FD => {FD, :close, [file,mode,perms]} options
|
172
|
-
options.
|
173
|
+
options.each do |key, val|
|
173
174
|
if fd?(key)
|
174
175
|
key = fd_to_io(key)
|
175
176
|
|
@@ -196,6 +197,11 @@ module POSIX
|
|
196
197
|
# { :chdir => '/' } in options means change into that dir
|
197
198
|
::Dir.chdir(options[:chdir]) if options[:chdir]
|
198
199
|
|
200
|
+
# { :pgroup => pgid } options
|
201
|
+
pgroup = options[:pgroup]
|
202
|
+
pgroup = 0 if pgroup == true
|
203
|
+
Process::setpgid(0, pgroup) if pgroup
|
204
|
+
|
199
205
|
# do the deed
|
200
206
|
::Kernel::exec(*argv)
|
201
207
|
ensure
|
data/lib/posix/spawn/child.rb
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
require 'posix/spawn'
|
2
2
|
|
3
|
-
class String
|
4
|
-
alias bytesize size
|
5
|
-
end unless ''.respond_to?(:bytesize)
|
6
|
-
|
7
3
|
module POSIX
|
8
4
|
module Spawn
|
9
5
|
# POSIX::Spawn::Child includes logic for executing child processes and
|
@@ -145,22 +141,32 @@ module POSIX
|
|
145
141
|
# Raises MaximumOutputExceeded when the total number of bytes output
|
146
142
|
# exceeds the amount specified by the max argument.
|
147
143
|
def read_and_write(input, stdin, stdout, stderr, timeout=nil, max=nil)
|
148
|
-
if input
|
149
|
-
input = input.dup.force_encoding('BINARY') if input.respond_to?(:force_encoding)
|
150
|
-
else
|
151
|
-
stdin.close
|
152
|
-
end
|
153
|
-
|
154
144
|
max = nil if max && max <= 0
|
155
145
|
out, err = '', ''
|
156
146
|
offset = 0
|
157
147
|
|
148
|
+
# force all string and IO encodings to BINARY under 1.9 for now
|
149
|
+
if out.respond_to?(:force_encoding)
|
150
|
+
[stdin, stdout, stderr].each do |fd|
|
151
|
+
fd.set_encoding('BINARY', 'BINARY')
|
152
|
+
end
|
153
|
+
out.force_encoding('BINARY')
|
154
|
+
err.force_encoding('BINARY')
|
155
|
+
input = input.dup.force_encoding('BINARY') if input
|
156
|
+
end
|
157
|
+
|
158
158
|
timeout = nil if timeout && timeout <= 0.0
|
159
159
|
@runtime = 0.0
|
160
160
|
start = Time.now
|
161
161
|
|
162
|
-
writers = input ? [stdin] : []
|
163
162
|
readers = [stdout, stderr]
|
163
|
+
writers =
|
164
|
+
if input
|
165
|
+
[stdin]
|
166
|
+
else
|
167
|
+
stdin.close
|
168
|
+
[]
|
169
|
+
end
|
164
170
|
t = timeout
|
165
171
|
while readers.any? || writers.any?
|
166
172
|
ready = IO.select(readers, writers, readers + writers, t)
|
@@ -171,11 +177,11 @@ module POSIX
|
|
171
177
|
begin
|
172
178
|
boom = nil
|
173
179
|
size = fd.write_nonblock(input)
|
174
|
-
input = input[size, input.
|
180
|
+
input = input[size, input.size]
|
175
181
|
rescue Errno::EPIPE => boom
|
176
182
|
rescue Errno::EAGAIN, Errno::EINTR
|
177
183
|
end
|
178
|
-
if boom || input.
|
184
|
+
if boom || input.size == 0
|
179
185
|
stdin.close
|
180
186
|
writers.delete(stdin)
|
181
187
|
end
|
data/lib/posix/spawn/version.rb
CHANGED
data/test/test_spawn.rb
CHANGED
@@ -155,7 +155,7 @@ module SpawnImplementationTests
|
|
155
155
|
end
|
156
156
|
|
157
157
|
def test_spawn_redirect_invalid_fds_raises_exception
|
158
|
-
pid = _spawn("echo", "hiya",
|
158
|
+
pid = _spawn("echo", "hiya", 1 => 250)
|
159
159
|
assert_process_exit_status pid, 127
|
160
160
|
rescue Errno::EBADF
|
161
161
|
# this happens on darwin only. GNU does spawn and exits 127.
|
@@ -269,6 +269,37 @@ module SpawnImplementationTests
|
|
269
269
|
File.unlink(file) rescue nil
|
270
270
|
end
|
271
271
|
|
272
|
+
##
|
273
|
+
# :pgroup => <pgid>
|
274
|
+
|
275
|
+
def test_spawn_inherit_pgroup_from_parent_by_default
|
276
|
+
pgrp = Process.getpgrp
|
277
|
+
pid = _spawn("ruby", "-e", "exit(Process.getpgrp == #{pgrp} ? 0 : 1)")
|
278
|
+
assert_process_exit_ok pid
|
279
|
+
end
|
280
|
+
|
281
|
+
def test_spawn_inherit_pgroup_from_parent_when_nil
|
282
|
+
pgrp = Process.getpgrp
|
283
|
+
pid = _spawn("ruby", "-e", "exit(Process.getpgrp == #{pgrp} ? 0 : 1)", :pgroup => nil)
|
284
|
+
assert_process_exit_ok pid
|
285
|
+
end
|
286
|
+
|
287
|
+
def test_spawn_new_pgroup_with_true
|
288
|
+
pid = _spawn("ruby", "-e", "exit(Process.getpgrp == $$ ? 0 : 1)", :pgroup => true)
|
289
|
+
assert_process_exit_ok pid
|
290
|
+
end
|
291
|
+
|
292
|
+
def test_spawn_new_pgroup_with_zero
|
293
|
+
pid = _spawn("ruby", "-e", "exit(Process.getpgrp == $$ ? 0 : 1)", :pgroup => 0)
|
294
|
+
assert_process_exit_ok pid
|
295
|
+
end
|
296
|
+
|
297
|
+
def test_spawn_explicit_pgroup
|
298
|
+
pgrp = Process.getpgrp
|
299
|
+
pid = _spawn("ruby", "-e", "exit(Process.getpgrp == #{pgrp} ? 0 : 1)", :pgroup => pgrp)
|
300
|
+
assert_process_exit_ok pid
|
301
|
+
end
|
302
|
+
|
272
303
|
##
|
273
304
|
# Exceptions
|
274
305
|
|
metadata
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: posix-spawn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
4
5
|
prerelease:
|
5
|
-
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
- 5
|
10
|
+
version: 0.3.5
|
6
11
|
platform: ruby
|
7
12
|
authors:
|
8
13
|
- Ryan Tomayko
|
@@ -11,7 +16,7 @@ autorequire:
|
|
11
16
|
bindir: bin
|
12
17
|
cert_chain: []
|
13
18
|
|
14
|
-
date: 2011-03-
|
19
|
+
date: 2011-03-25 00:00:00 -07:00
|
15
20
|
default_executable:
|
16
21
|
dependencies:
|
17
22
|
- !ruby/object:Gem::Dependency
|
@@ -22,6 +27,11 @@ dependencies:
|
|
22
27
|
requirements:
|
23
28
|
- - "="
|
24
29
|
- !ruby/object:Gem::Version
|
30
|
+
hash: 15
|
31
|
+
segments:
|
32
|
+
- 0
|
33
|
+
- 7
|
34
|
+
- 6
|
25
35
|
version: 0.7.6
|
26
36
|
type: :development
|
27
37
|
version_requirements: *id001
|
@@ -71,12 +81,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
71
81
|
requirements:
|
72
82
|
- - ">="
|
73
83
|
- !ruby/object:Gem::Version
|
84
|
+
hash: 3
|
85
|
+
segments:
|
86
|
+
- 0
|
74
87
|
version: "0"
|
75
88
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
89
|
none: false
|
77
90
|
requirements:
|
78
91
|
- - ">="
|
79
92
|
- !ruby/object:Gem::Version
|
93
|
+
hash: 3
|
94
|
+
segments:
|
95
|
+
- 0
|
80
96
|
version: "0"
|
81
97
|
requirements: []
|
82
98
|
|