Skip to content

Commit 29dd67b

Browse files
committed
wip
1 parent 3f7d4bc commit 29dd67b

8 files changed

Lines changed: 759 additions & 59 deletions

File tree

include/ruby/win32.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,16 @@ extern rb_pid_t rb_w32_uaspawn_flags(int, const char *, char *const *, DWORD);
328328
extern int kill(rb_pid_t, int);
329329
extern int fcntl(int, int, ...);
330330
extern int rb_w32_set_nonblock(int);
331+
/* Accessor for the CRT internal _osfile array so that the inherit
332+
* table builder in process.c can read the FNOINHERIT bit set by
333+
* fcntl(F_SETFD, FD_CLOEXEC). */
334+
extern unsigned char rb_w32_get_osfile(int);
335+
/* Mirror of fcntl(fd, F_SETFD, FD_CLOEXEC / cleared) on Windows.
336+
* Sets both the OS HANDLE_FLAG_INHERIT bit and the CRT _osfile
337+
* FNOINHERIT bit so non-standard fds are not inherited by child
338+
* processes unless explicitly requested. Returns 0 on success, -1
339+
* (with errno set) if SetHandleInformation fails. */
340+
extern int rb_w32_set_cloexec(int fd, int cloexec);
331341
extern rb_pid_t rb_w32_getpid(void);
332342
extern rb_pid_t rb_w32_getppid(void);
333343
extern int rb_w32_isatty(int);

internal/io.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ VALUE rb_io_prep_stderr(void);
151151
int rb_io_notify_close(struct rb_io *fptr);
152152
bool rb_io_fptr_finalize_closed(struct rb_io *fptr);
153153

154+
int rb_get_max_fd(void);
155+
154156
RUBY_SYMBOL_EXPORT_BEGIN
155157
/* io.c (export) */
156158
void rb_maygvl_fd_fix_cloexec(int fd);

internal/process.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,17 @@ VALUE rb_execarg_extract_options(VALUE execarg_obj, VALUE opthash);
100100
void rb_execarg_setenv(VALUE execarg_obj, VALUE env);
101101
RUBY_SYMBOL_EXPORT_END
102102

103+
#if defined(_WIN32)
104+
/* process.c (Windows only): build/free the file descriptor inheritance
105+
* snapshot passed to CreateProcessW via lpReserved2. These are used by
106+
* both process.c and io.c (pipe_open). The full definition lives in
107+
* win32/file.h, which is not pulled in here to keep this header portable. */
108+
struct rb_w32_inherit_actions;
109+
void rb_w32_build_inherit_actions(const struct rb_execarg *eargp,
110+
struct rb_w32_inherit_actions *actions);
111+
void rb_w32_free_inherit_actions(struct rb_w32_inherit_actions *actions);
112+
#endif
113+
103114
/* argv_str contains extra two elements.
104115
* The beginning one is for /bin/sh used by exec_with_sh.
105116
* The last one for terminating NULL used by execve.

io.c

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,27 @@ rb_update_max_fd(int fd)
276276
void
277277
rb_maygvl_fd_fix_cloexec(int fd)
278278
{
279+
#ifdef _WIN32
280+
/* Use the dedicated rb_w32_set_cloexec helper instead of the fcntl
281+
* path below, so that both the OS-level HANDLE_FLAG_INHERIT bit and
282+
* the CRT _osfile FNOINHERIT bit are kept in sync. The standard
283+
* handles (0, 1, 2) stay inheritable, while every other fd is made
284+
* non-inheritable (close-on-exec) by default. This matches the
285+
* assumption elsewhere (notably the lpReserved2 fd-inheritance
286+
* machinery) that a fd is inherited by a child only when
287+
* close_on_exec was explicitly cleared. */
288+
if (fd <= 2) {
289+
if (rb_w32_set_cloexec(fd, FALSE) != 0)
290+
rb_bug("rb_maygvl_fd_fix_cloexec: rb_w32_set_cloexec(%d, FALSE) failed: %s",
291+
fd, strerror(errno));
292+
}
293+
else {
294+
if (rb_w32_set_cloexec(fd, TRUE) != 0)
295+
rb_bug("rb_maygvl_fd_fix_cloexec: rb_w32_set_cloexec(%d, TRUE) failed: %s",
296+
fd, strerror(errno));
297+
}
298+
return;
299+
#endif
279300
/* MinGW don't have F_GETFD and FD_CLOEXEC. [ruby-core:40281] */
280301
#if defined(HAVE_FCNTL) && defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
281302
int flags, flags2, ret;
@@ -303,6 +324,13 @@ rb_fd_fix_cloexec(int fd)
303324
rb_update_max_fd(fd);
304325
}
305326

327+
/* License: Ruby's */
328+
int
329+
rb_get_max_fd(void)
330+
{
331+
return (int)max_file_descriptor;
332+
}
333+
306334
/* this is only called once */
307335
static int
308336
rb_fix_detect_o_cloexec(int fd)
@@ -7705,18 +7733,35 @@ pipe_open(VALUE execarg_obj, const char *modestr, enum rb_io_mode fmode,
77057733
# if defined(HAVE_SPAWNVE)
77067734
if (eargp->envp_str) envp = (char **)RSTRING_PTR(eargp->envp_str);
77077735
# endif
7708-
while ((pid = DO_SPAWN(cmd, args, envp)) < 0) {
7709-
/* exec failed */
7710-
switch (e = errno) {
7711-
case EAGAIN:
7736+
# if defined(_WIN32)
7737+
/* On Windows, spawn through the inherit-table path so that
7738+
* close_on_exec = false fds (and any explicit fd_dup2 redirects
7739+
* set up above) propagate to the child via lpReserved2. */
7740+
struct rb_w32_inherit_actions actions;
7741+
rb_w32_build_inherit_actions(eargp, &actions);
7742+
if (args) {
7743+
pid = rb_w32_uaspawn_inherit(P_NOWAIT,
7744+
cmd, args, 0, CP_UTF8, &actions);
7745+
}
7746+
else {
7747+
pid = rb_w32_uspawn_inherit(P_NOWAIT, cmd, NULL,
7748+
CP_UTF8, &actions);
7749+
}
7750+
rb_w32_free_inherit_actions(&actions);
7751+
# else
7752+
while ((pid = DO_SPAWN(cmd, args, envp)) < 0) {
7753+
/* exec failed */
7754+
switch (e = errno) {
7755+
case EAGAIN:
77127756
# if EWOULDBLOCK != EAGAIN
7713-
case EWOULDBLOCK:
7757+
case EWOULDBLOCK:
77147758
# endif
7715-
rb_thread_sleep(1);
7716-
continue;
7759+
rb_thread_sleep(1);
7760+
continue;
7761+
}
7762+
break;
77177763
}
7718-
break;
7719-
}
7764+
# endif
77207765
if (eargp)
77217766
rb_execarg_run_options(sargp, NULL, NULL, 0);
77227767
# endif
@@ -7738,9 +7783,7 @@ pipe_open(VALUE execarg_obj, const char *modestr, enum rb_io_mode fmode,
77387783

77397784
/* parent */
77407785
if (pid < 0) {
7741-
# if defined(HAVE_WORKING_FORK)
77427786
e = errno;
7743-
# endif
77447787
close(arg.pair[0]);
77457788
close(arg.pair[1]);
77467789
if ((fmode & (FMODE_READABLE|FMODE_WRITABLE)) == (FMODE_READABLE|FMODE_WRITABLE)) {

0 commit comments

Comments
 (0)