posix-spawn 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|