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 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]
@@ -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
- posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK);
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);
@@ -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) && ![:chdir,:unsetenv_others].include?(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.map do |key, val|
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
@@ -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.bytesize]
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.bytesize == 0
184
+ if boom || input.size == 0
179
185
  stdin.close
180
186
  writers.delete(stdin)
181
187
  end
@@ -1,5 +1,5 @@
1
1
  module POSIX
2
2
  module Spawn
3
- VERSION = '0.3.3'
3
+ VERSION = '0.3.5'
4
4
  end
5
5
  end
@@ -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", 250 => 3)
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
- version: 0.3.3
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-05 00:00:00 -08:00
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