Skip to content

Commit c2b8100

Browse files
committed
wip
1 parent 3f7d4bc commit c2b8100

7 files changed

Lines changed: 843 additions & 53 deletions

File tree

include/ruby/win32.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,11 +323,46 @@ extern rb_pid_t wait(int *);
323323
extern rb_pid_t rb_w32_uspawn(int, const char *, const char*);
324324
extern rb_pid_t rb_w32_uaspawn(int, const char *, char *const *);
325325
extern rb_pid_t rb_w32_uaspawn_flags(int, const char *, char *const *, DWORD);
326+
327+
/* Opaque builder for the Windows child-process redirection requests.
328+
*
329+
* On Windows, the C runtime propagates a list of inheritable file handles to
330+
* a spawned child through the reserved STARTUPINFOW fields lpReserved2 /
331+
* cbReserved2. The concrete snapshot is built inside win32.c from the
332+
* close_on_exec state of every open descriptor; callers describe the
333+
* redirections only through the accessors below and pass the resulting
334+
* pointer to the spawn functions, so they never see the CRT-internal fd
335+
* details. The struct layout is private to win32.c. */
336+
struct rb_w32_spawn_actions;
337+
extern struct rb_w32_spawn_actions *rb_w32_spawn_actions_init(void);
338+
extern void rb_w32_spawn_actions_destroy(struct rb_w32_spawn_actions *actions);
339+
extern void rb_w32_spawn_actions_addclose(struct rb_w32_spawn_actions *actions, int fd);
340+
extern void rb_w32_spawn_actions_adddup2(struct rb_w32_spawn_actions *actions,
341+
int oldfd, int newfd);
342+
extern void rb_w32_spawn_actions_adddup2_child(struct rb_w32_spawn_actions *actions,
343+
int oldfd, int newfd);
344+
345+
extern rb_pid_t rb_w32_uaspawn_inherit(int mode, const char *prog, char *const *argv,
346+
DWORD flags, UINT cp,
347+
const struct rb_w32_spawn_actions *actions);
348+
extern rb_pid_t rb_w32_uspawn_inherit(int mode, const char *cmd, const char *prog,
349+
UINT cp,
350+
const struct rb_w32_spawn_actions *actions);
326351
#undef HAVE_KILL
327352
#define HAVE_KILL 1
328353
extern int kill(rb_pid_t, int);
329354
extern int fcntl(int, int, ...);
330355
extern int rb_w32_set_nonblock(int);
356+
/* Accessor for the CRT internal _osfile array so that the inherit
357+
* table builder in process.c can read the FNOINHERIT bit set by
358+
* fcntl(F_SETFD, FD_CLOEXEC). */
359+
extern unsigned char rb_w32_get_osfile(int);
360+
/* Mirror of fcntl(fd, F_SETFD, FD_CLOEXEC / cleared) on Windows.
361+
* Sets both the OS HANDLE_FLAG_INHERIT bit and the CRT _osfile
362+
* FNOINHERIT bit so non-standard fds are not inherited by child
363+
* processes unless explicitly requested. Returns 0 on success, -1
364+
* (with errno set) if SetHandleInformation fails. */
365+
extern int rb_w32_set_cloexec(int fd, int cloexec);
331366
extern rb_pid_t rb_w32_getpid(void);
332367
extern rb_pid_t rb_w32_getppid(void);
333368
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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ 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 the opaque redirection requests consumed by
105+
* win32.c to construct the lpReserved2 inherit table. Used by both process.c
106+
* and io.c (pipe_open). The struct is opaque (defined in win32.c); only a
107+
* forward declaration is needed here to keep this header portable. */
108+
struct rb_w32_spawn_actions;
109+
struct rb_w32_spawn_actions *rb_w32_build_spawn_actions(const struct rb_execarg *eargp);
110+
#endif
111+
103112
/* argv_str contains extra two elements.
104113
* The beginning one is for /bin/sh used by exec_with_sh.
105114
* The last one for terminating NULL used by execve.

io.c

Lines changed: 44 additions & 2 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,6 +7733,21 @@ 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
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_spawn_actions *actions = rb_w32_build_spawn_actions(eargp);
7741+
if (args) {
7742+
pid = rb_w32_uaspawn_inherit(P_NOWAIT,
7743+
cmd, args, 0, CP_UTF8, actions);
7744+
}
7745+
else {
7746+
pid = rb_w32_uspawn_inherit(P_NOWAIT, cmd, NULL,
7747+
CP_UTF8, actions);
7748+
}
7749+
rb_w32_spawn_actions_destroy(actions);
7750+
# else
77087751
while ((pid = DO_SPAWN(cmd, args, envp)) < 0) {
77097752
/* exec failed */
77107753
switch (e = errno) {
@@ -7717,6 +7760,7 @@ pipe_open(VALUE execarg_obj, const char *modestr, enum rb_io_mode fmode,
77177760
}
77187761
break;
77197762
}
7763+
# endif
77207764
if (eargp)
77217765
rb_execarg_run_options(sargp, NULL, NULL, 0);
77227766
# endif
@@ -7738,9 +7782,7 @@ pipe_open(VALUE execarg_obj, const char *modestr, enum rb_io_mode fmode,
77387782

77397783
/* parent */
77407784
if (pid < 0) {
7741-
# if defined(HAVE_WORKING_FORK)
77427785
e = errno;
7743-
# endif
77447786
close(arg.pair[0]);
77457787
close(arg.pair[1]);
77467788
if ((fmode & (FMODE_READABLE|FMODE_WRITABLE)) == (FMODE_READABLE|FMODE_WRITABLE)) {

0 commit comments

Comments
 (0)