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 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