Bug #6131 ยป process.patch
| include/ruby/win32.h | ||
|---|---|---|
| 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); | ||
| process.c | ||
|---|---|---|
| #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 | ||
| ... | ... | |
| #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; | ||
| ... | ... | |
| 	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; | ||
| } | ||
| ... | ... | |
|     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) | ||
| { | ||
| ... | ... | |
|         } | ||
|         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) { | ||
| ... | ... | |
| 	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) | ||
| ... | ... | |
|  *        :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] | ||
| ... | ... | |
|  *  If a hash is given as +options+, | ||
|  *  it specifies | ||
|  *  process group, | ||
|  *  create new process group, | ||
|  *  resource limit, | ||
|  *  current directory, | ||
|  *  umask and | ||
| ... | ... | |
|  *    pid = spawn(command, :pgroup=>true) # process leader | ||
|  *    pid = spawn(command, :pgroup=>10) # belongs to the process group 10 | ||
|  * | ||
|  *  The <code>:new_pgroup</code> key in +options+ specifies to pass | ||
|  *  +CREATE_NEW_PROCESS_GROUP+ flag to <code>CreateProcessW()</code> 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 | ||
|  *  <code>Process.kill(:SIGINT, pid)</code> 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 <code>:rlimit_</code><em>foo</em> key specifies a resource limit. | ||
|  *  <em>foo</em> should be one of resource types such as <code>core</code>. | ||
|  *  The corresponding value should be an integer or an array which have one or | ||
| test/ruby/test_process.rb | ||
|---|---|---|
|       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 | ||
| test/ruby/test_thread.rb | ||
|---|---|---|
|     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) | ||
| win32/win32.c | ||
|---|---|---|
| #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); | ||
| ... | ... | |
| /* 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; | ||
| ... | ... | |
| 	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 */ | ||
| ... | ... | |
|     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; | ||
| ... | ... | |
| /* 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; | ||
| ... | ... | |
|     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; | ||