diff --git a/include/ruby/win32.h b/include/ruby/win32.h
index d16010c..302bf5e 100644
--- a/include/ruby/win32.h
+++ b/include/ruby/win32.h
@@ -285,6 +285,7 @@ extern int gettimeofday(struct timeval *, struct timezone *);
extern rb_pid_t waitpid (rb_pid_t, int *, int);
extern rb_pid_t rb_w32_spawn(int, const char *, const char*);
extern rb_pid_t rb_w32_aspawn(int, const char *, char *const *);
+extern rb_pid_t rb_w32_aspawn_flags(int, const char *, char *const *, DWORD);
extern int kill(int, int);
extern int fcntl(int, int, ...);
extern rb_pid_t rb_w32_getpid(void);
diff --git a/process.c b/process.c
index 390cbdb..7735ecb 100644
--- a/process.c
+++ b/process.c
@@ -1207,6 +1207,21 @@ rb_proc_exec(const char *str)
#endif /* _WIN32 */
}
+enum {
+ EXEC_OPTION_PGROUP,
+ EXEC_OPTION_RLIMIT,
+ EXEC_OPTION_UNSETENV_OTHERS,
+ EXEC_OPTION_ENV,
+ EXEC_OPTION_CHDIR,
+ EXEC_OPTION_UMASK,
+ EXEC_OPTION_DUP2,
+ EXEC_OPTION_CLOSE,
+ EXEC_OPTION_OPEN,
+ EXEC_OPTION_DUP2_CHILD,
+ EXEC_OPTION_CLOSE_OTHERS,
+ EXEC_OPTION_NEW_PGROUP
+};
+
#if defined(_WIN32)
#define HAVE_SPAWNV 1
#endif
@@ -1252,7 +1267,7 @@ proc_spawn_v(char **argv, char *prog)
#endif
static rb_pid_t
-proc_spawn_n(int argc, VALUE *argv, VALUE prog)
+proc_spawn_n(int argc, VALUE *argv, VALUE prog, VALUE options)
{
char **args;
int i;
@@ -1264,8 +1279,17 @@ proc_spawn_n(int argc, VALUE *argv, VALUE prog)
args[i] = RSTRING_PTR(argv[i]);
}
args[i] = (char*) 0;
- if (args[0])
+ if (args[0]) {
+#if defined(_WIN32)
+ DWORD flags = 0;
+ if (RTEST(rb_ary_entry(options, EXEC_OPTION_NEW_PGROUP))) {
+ flags = CREATE_NEW_PROCESS_GROUP;
+ }
+ pid = rb_w32_aspawn_flags(P_NOWAIT, prog ? RSTRING_PTR(prog) : 0, args, flags);
+#else
pid = proc_spawn_v(args, prog ? RSTRING_PTR(prog) : 0);
+#endif
+ }
ALLOCV_END(v);
return pid;
}
@@ -1313,20 +1337,6 @@ hide_obj(VALUE obj)
return obj;
}
-enum {
- EXEC_OPTION_PGROUP,
- EXEC_OPTION_RLIMIT,
- EXEC_OPTION_UNSETENV_OTHERS,
- EXEC_OPTION_ENV,
- EXEC_OPTION_CHDIR,
- EXEC_OPTION_UMASK,
- EXEC_OPTION_DUP2,
- EXEC_OPTION_CLOSE,
- EXEC_OPTION_OPEN,
- EXEC_OPTION_DUP2_CHILD,
- EXEC_OPTION_CLOSE_OTHERS
-};
-
static VALUE
check_exec_redirect_fd(VALUE v, int iskey)
{
@@ -1510,6 +1520,16 @@ rb_exec_arg_addopt(struct rb_exec_arg *e, VALUE key, VALUE val)
}
else
#endif
+#ifdef _WIN32
+ if (id == rb_intern("new_pgroup")) {
+ if (!NIL_P(rb_ary_entry(options, EXEC_OPTION_NEW_PGROUP))) {
+ rb_raise(rb_eArgError, "new_pgroup option specified twice");
+ }
+ val = RTEST(val) ? Qtrue : Qfalse;
+ rb_ary_store(options, EXEC_OPTION_NEW_PGROUP, val);
+ }
+ else
+#endif
#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
if (strncmp("rlimit_", rb_id2name(id), 7) == 0 &&
(rtype = rlimit_type_by_lname(rb_id2name(id)+7)) != -1) {
@@ -3006,7 +3026,7 @@ rb_spawn_process(struct rb_exec_arg *earg, VALUE prog, char *errmsg, size_t errm
pid = proc_spawn(RSTRING_PTR(prog));
}
else {
- pid = proc_spawn_n(argc, argv, prog);
+ pid = proc_spawn_n(argc, argv, prog, earg->options);
}
# if defined(_WIN32)
if (pid == -1)
@@ -3141,6 +3161,9 @@ rb_f_system(int argc, VALUE *argv)
* :pgroup => true or 0 : make a new process group
* :pgroup => pgid : join to specified process group
* :pgroup => nil : don't change the process group (default)
+ * create new process group: Windows only
+ * :new_pgroup => true : the new process is the root process of a new process group
+ * :new_pgroup => false : don't create a new process group (default)
* resource limit: resourcename is core, cpu, data, etc. See Process.setrlimit.
* :rlimit_resourcename => limit
* :rlimit_resourcename => [cur_limit, max_limit]
@@ -3179,6 +3202,7 @@ rb_f_system(int argc, VALUE *argv)
* If a hash is given as +options+,
* it specifies
* process group,
+ * create new process group,
* resource limit,
* current directory,
* umask and
@@ -3200,6 +3224,17 @@ rb_f_system(int argc, VALUE *argv)
* pid = spawn(command, :pgroup=>true) # process leader
* pid = spawn(command, :pgroup=>10) # belongs to the process group 10
*
+ * The :new_pgroup
key in +options+ specifies to pass
+ * +CREATE_NEW_PROCESS_GROUP+ flag to CreateProcessW()
that is
+ * Windows API. This option is only for Windows.
+ * true means the new process is the root process of the new process group.
+ * The new process has CTRL+C disabled. This flag is necessary for
+ * Process.kill(:SIGINT, pid)
on the subprocess.
+ * :new_pgroup is false by default.
+ *
+ * pid = spawn(command, :new_pgroup=>true) # new process group
+ * pid = spawn(command, :new_pgroup=>false) # same process group
+ *
* The :rlimit_
foo key specifies a resource limit.
* foo should be one of resource types such as core
.
* The corresponding value should be an integer or an array which have one or
diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb
index 5882e6a..34c1355 100644
--- a/test/ruby/test_process.rb
+++ b/test/ruby/test_process.rb
@@ -1360,4 +1360,13 @@ class TestProcess < Test::Unit::TestCase
assert(io.close_on_exec?)
}
end
+
+ def test_execopts_new_pgroup
+ return unless windows?
+
+ assert_nothing_raised { system(*TRUECOMMAND, :new_pgroup=>true) }
+ assert_nothing_raised { system(*TRUECOMMAND, :new_pgroup=>false) }
+ assert_nothing_raised { spawn(*TRUECOMMAND, :new_pgroup=>true) }
+ assert_nothing_raised { IO.popen([*TRUECOMMAND, :new_pgroup=>true]) {} }
+ end
end
diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb
index 8a32868..b6514cf 100644
--- a/test/ruby/test_thread.rb
+++ b/test/ruby/test_thread.rb
@@ -685,7 +685,9 @@ class TestThreadGroup < Test::Unit::TestCase
t0 = Time.now.to_f
pid = nil
cmd = 'r,=IO.pipe; Thread.start {Thread.pass until Thread.main.stop?; puts; STDOUT.flush}; r.read'
- s, err = EnvUtil.invoke_ruby(['-e', cmd], "", true, true) do |in_p, out_p, err_p, cpid|
+ opt = {}
+ opt[:new_pgroup] = true if /mswin|mingw/ =~ RUBY_PLATFORM
+ s, err = EnvUtil.invoke_ruby(['-e', cmd], "", true, true, opt) do |in_p, out_p, err_p, cpid|
out_p.gets
pid = cpid
Process.kill(:SIGINT, pid)
diff --git a/win32/win32.c b/win32/win32.c
index 310eb85..82ed110 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -72,7 +72,7 @@
#define TO_SOCKET(x) _get_osfhandle(x)
-static struct ChildRecord *CreateChild(const WCHAR *, const WCHAR *, SECURITY_ATTRIBUTES *, HANDLE, HANDLE, HANDLE);
+static struct ChildRecord *CreateChild(const WCHAR *, const WCHAR *, SECURITY_ATTRIBUTES *, HANDLE, HANDLE, HANDLE, DWORD);
static int has_redirection(const char *);
int rb_w32_wait_events(HANDLE *events, int num, DWORD timeout);
static int rb_w32_open_osfhandle(intptr_t osfhandle, int flags);
@@ -1087,10 +1087,9 @@ child_result(struct ChildRecord *child, int mode)
/* License: Ruby's */
static struct ChildRecord *
CreateChild(const WCHAR *cmd, const WCHAR *prog, SECURITY_ATTRIBUTES *psa,
- HANDLE hInput, HANDLE hOutput, HANDLE hError)
+ HANDLE hInput, HANDLE hOutput, HANDLE hError, DWORD dwCreationFlags)
{
BOOL fRet;
- DWORD dwCreationFlags;
STARTUPINFOW aStartupInfo;
PROCESS_INFORMATION aProcessInformation;
SECURITY_ATTRIBUTES sa;
@@ -1137,7 +1136,7 @@ CreateChild(const WCHAR *cmd, const WCHAR *prog, SECURITY_ATTRIBUTES *psa,
aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
}
- dwCreationFlags = (CREATE_NEW_PROCESS_GROUP | NORMAL_PRIORITY_CLASS);
+ dwCreationFlags |= NORMAL_PRIORITY_CLASS;
if (lstrlenW(cmd) > 32767) {
child->pid = 0; /* release the slot */
@@ -1290,7 +1289,7 @@ rb_w32_spawn(int mode, const char *cmd, const char *prog)
wshell = shell ? acp_to_wstr(shell, NULL) : NULL;
if (v2) ALLOCV_END(v2);
- ret = child_result(CreateChild(wcmd, wshell, NULL, NULL, NULL, NULL), mode);
+ ret = child_result(CreateChild(wcmd, wshell, NULL, NULL, NULL, NULL, 0), mode);
free(wshell);
free(wcmd);
return ret;
@@ -1298,7 +1297,7 @@ rb_w32_spawn(int mode, const char *cmd, const char *prog)
/* License: Artistic or GPL */
rb_pid_t
-rb_w32_aspawn(int mode, const char *prog, char *const *argv)
+rb_w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
{
int c_switch = 0;
size_t len;
@@ -1357,12 +1356,18 @@ rb_w32_aspawn(int mode, const char *prog, char *const *argv)
if (v) ALLOCV_END(v);
wprog = prog ? acp_to_wstr(prog, NULL) : NULL;
- ret = child_result(CreateChild(wcmd, wprog, NULL, NULL, NULL, NULL), mode);
+ ret = child_result(CreateChild(wcmd, wprog, NULL, NULL, NULL, NULL, flags), mode);
free(wprog);
free(wcmd);
return ret;
}
+rb_pid_t
+rb_w32_aspawn(int mode, const char *prog, char *const *argv)
+{
+ return rb_w32_aspawn_flags(mode, prog, argv, 0);
+}
+
/* License: Artistic or GPL */
typedef struct _NtCmdLineElement {
struct _NtCmdLineElement *next;