1 /* dtable.cc: file descriptor support.
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
12 #define __INSIDE_CYGWIN_NET__
15 #include <sys/socket.h>
21 #define USE_SYS_TYPES_FD_SET
25 #include "perprocess.h"
33 #include "shared_info.h"
35 static const NO_COPY DWORD std_consts[] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
38 static bool handle_to_fn (HANDLE, char *);
40 #define WCLEN(x) ((sizeof (x) / sizeof (WCHAR)) - 1)
41 static const char unknown_file[] = "some disk file";
42 static const WCHAR DEV_NULL[] = L"\\Device\\Null";
43 static const WCHAR DEV_SOCKET[] = L"\\Device\\Afd";
45 static const WCHAR DEVICE_PREFIX[] = L"\\device\\";
46 static const size_t DEVICE_PREFIX_LEN WCLEN (DEVICE_PREFIX);
48 static const WCHAR DEV_NAMED_PIPE[] = L"\\Device\\NamedPipe\\";
49 static const size_t DEV_NAMED_PIPE_LEN = WCLEN (DEV_NAMED_PIPE);
51 static const WCHAR DEV_REMOTE[] = L"\\Device\\LanmanRedirector\\";
52 static const size_t DEV_REMOTE_LEN = WCLEN (DEV_REMOTE);
54 static const WCHAR DEV_REMOTE1[] = L"\\Device\\WinDfs\\Root\\";
55 static const size_t DEV_REMOTE1_LEN = WCLEN (DEV_REMOTE1);
57 /* Set aside space for the table of fds */
61 if (!cygheap->fdtab.size)
62 cygheap->fdtab.extend (NOFILE_INCR);
66 set_std_handle (int fd)
69 SetStdHandle (std_consts[fd], cygheap->fdtab[fd]->get_handle ());
71 SetStdHandle (std_consts[fd], cygheap->fdtab[fd]->get_output_handle ());
75 dtable::extend (int howmuch)
77 int new_size = size + howmuch;
78 fhandler_base **newfds;
83 if (new_size > OPEN_MAX_MAX)
89 /* Try to allocate more space for fd table. We can't call realloc ()
90 here to preserve old table if memory allocation fails */
92 if (!(newfds = (fhandler_base **) ccalloc (HEAP_ARGV, new_size, sizeof newfds[0])))
94 debug_printf ("calloc failed");
100 memcpy (newfds, fds, size * sizeof (fds[0]));
106 debug_printf ("size %d, fds %p", size, fds);
111 dtable::get_debugger_info ()
113 extern bool jit_debug;
114 if (!jit_debug && being_debugged ())
116 char std[3][sizeof ("/dev/ptyNNNN")];
117 std[0][0] = std[1][0] = std [2][0] = '\0';
118 char buf[sizeof ("cYgstd %x") + 32];
119 sprintf (buf, "cYgstd %x %x %x", (unsigned) &std, sizeof (std[0]), 3);
120 OutputDebugString (buf);
121 for (int i = 0; i < 3; i++)
124 HANDLE h = GetStdHandle (std_consts[i]);
125 fhandler_base *fh = build_fh_name (std[i]);
129 if (!fh->open ((i ? (i == 2 ? O_RDWR : O_WRONLY) : O_RDONLY)
135 /* Copy to Windows' idea of a standard handle, otherwise
136 we have invalid standard handles when calling Windows
137 functions (small_printf and strace might suffer, too). */
138 SetStdHandle (std_consts[i], i ? fh->get_output_handle ()
139 : fh->get_handle ());
145 /* Initialize the file descriptor/handle mapping table.
146 This function should only be called when a cygwin function is invoked
147 by a non-cygwin function, i.e., it should only happen very rarely. */
150 dtable::stdio_init ()
152 /* Set these before trying to output anything from strace.
153 Also, always set them even if we're to pick up our parent's fds
154 in case they're missed. */
156 if (myself->cygstarted || ISSTATE (myself, PID_CYGPARENT))
158 tty_min *t = cygwin_shared->tty.get_cttyp ();
159 if (t && t->getpgid () == myself->pid && t->is_console)
160 init_console_handler (true);
164 HANDLE in = GetStdHandle (STD_INPUT_HANDLE);
165 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
166 HANDLE err = GetStdHandle (STD_ERROR_HANDLE);
168 init_std_file_from_handle (0, in);
170 /* STD_ERROR_HANDLE has been observed to be the same as
171 STD_OUTPUT_HANDLE. We need separate handles (e.g. using pipes
172 to pass data from child to parent). */
173 /* CV 2008-10-17: Under debugger control, std fd's have been potentially
174 initialized in dtable::get_debugger_info (). In this case
175 init_std_file_from_handle is a no-op, so, even if out == err we don't
176 want to duplicate the handle since it will be unused. */
177 if (out == err && (!being_debugged () || not_open (2)))
179 /* Since this code is not invoked for forked tasks, we don't have
180 to worry about the close-on-exec flag here. */
181 if (!DuplicateHandle (GetCurrentProcess (), out,
182 GetCurrentProcess (), &err,
183 0, TRUE, DUPLICATE_SAME_ACCESS))
185 /* If that fails, do this as a fall back. */
187 system_printf ("couldn't make stderr distinct from stdout, %E");
191 init_std_file_from_handle (1, out);
192 init_std_file_from_handle (2, err);
195 const int dtable::initial_archetype_size;
198 dtable::find_archetype (device& dev)
200 for (unsigned i = 0; i < farchetype; i++)
201 if (archetypes[i]->get_device () == (DWORD) dev)
202 return archetypes[i];
207 dtable::add_archetype ()
209 if (farchetype++ >= narchetypes)
210 archetypes = (fhandler_base **) crealloc_abort (archetypes, (narchetypes += initial_archetype_size) * sizeof archetypes[0]);
211 return archetypes + farchetype - 1;
215 dtable::delete_archetype (fhandler_base *fh)
217 for (unsigned i = 0; i < farchetype; i++)
218 if (fh == archetypes[i])
220 debug_printf ("deleting element %d for %s", i, fh->get_name ());
221 if (i < --farchetype)
222 archetypes[i] = archetypes[farchetype];
230 dtable::find_unused_handle (int start)
234 for (size_t i = start; i < size; i++)
235 /* See if open -- no need for overhead of not_open */
239 while (extend (NOFILE_INCR));
244 dtable::release (int fd)
248 if (fds[fd]->need_fixup_before ())
249 dec_need_fixup_before ();
256 cygwin_attach_handle_to_fd (char *name, int fd, HANDLE handle, mode_t bin,
260 fd = cygheap->fdtab.find_unused_handle ();
261 fhandler_base *fh = build_fh_name (name);
262 cygheap->fdtab[fd] = fh;
263 fh->init (handle, myaccess, bin ?: fh->pc_binmode ());
268 dtable::init_std_file_from_handle (int fd, HANDLE handle)
270 CONSOLE_SCREEN_BUFFER_INFO buf;
272 unsigned bin = O_BINARY;
275 first_fd_for_open = 0;
282 DWORD ft = GetFileType (handle);
283 char name[NT_MAX_PATH];
285 if (ft == FILE_TYPE_UNKNOWN && GetLastError () == ERROR_INVALID_HANDLE)
286 /* can't figure out what this is */;
287 else if (ft == FILE_TYPE_PIPE)
289 int rcv = 0, len = sizeof (int);
291 if (handle_to_fn (handle, name))
293 else if (strcmp (name, ":sock:") == 0
294 /* NtQueryObject returns an error when called on an LSP socket
295 handle. While fdsock now tries to fetch the underlying
296 base socket, this only works on Vista and later. */
297 || (strcmp (name, unknown_file) == 0
298 && !::getsockopt ((SOCKET) handle, SOL_SOCKET, SO_RCVBUF,
299 (char *) &rcv, &len)))
310 else if (GetConsoleScreenBufferInfo (handle, &buf)
311 || GetNumberOfConsoleInputEvents (handle, (DWORD *) &buf))
314 if (myself->ctty > 0)
315 dev.parse (myself->ctty);
317 dev.parse (FH_CONSOLE);
319 else if (GetCommState (handle, &dcb))
320 /* FIXME: Not right - assumes ttyS0 */
321 dev.parse (DEV_SERIAL_MAJOR, 0);
323 /* Try to figure it out from context - probably a disk file */
324 handle_to_fn (handle, name);
326 if (!name[0] && !dev)
333 fh = build_fh_dev (dev);
335 fh = build_fh_name (name);
339 bin = fh->pc_binmode ();
342 bin = fh->get_default_fmode (O_RDWR);
349 FILE_ACCESS_INFORMATION fai;
351 /* Console windows are not kernel objects, so the access mask returned
352 by NtQueryInformationFile is meaningless. CMD always hands down
353 stdin handles as R/O handles, but our tty slave sides are R/W. */
354 if (dev == FH_TTY || iscons_dev (dev) || dev.get_major () == DEV_TTYS_MAJOR)
355 access |= GENERIC_READ | GENERIC_WRITE;
356 else if (NT_SUCCESS (NtQueryInformationFile (handle, &io, &fai,
358 FileAccessInformation)))
360 if (fai.AccessFlags & FILE_READ_DATA)
361 access |= GENERIC_READ;
362 if (fai.AccessFlags & FILE_WRITE_DATA)
363 access |= GENERIC_WRITE;
366 access |= GENERIC_READ;
368 access |= GENERIC_WRITE; /* Should be rdwr for stderr but not sure that's
369 possible for some versions of handles */
370 /* FIXME: Workaround Windows 7 issue. If the parent process of
371 the process tree closes the original handles to the console window,
372 strange problems occur when starting child processes later on if
373 stdio redirection is used.
375 CV 2009-08-08: It looks like this problem has been fixed only
376 half-heartedly in RTM. Unfortunately the new implementation
377 has still a problem which now also occurs on the 32 bit release
378 of Windows 7. It's still not quite clear what happens but it's
379 easily reproducible. Just start X via the start menu entry.
380 This opens an xterm window with a shell. Exit from the shell,
381 and you get a Windows error box reporting a crash in the
382 Console Window Host application (conhost.exe) due to an access
385 This needs further investigation but the workaround not to close
386 the handles will have a marginal hit of three extra handles per
388 if (!fh->init (iscons_dev (dev) && wincap.has_console_handle_problem ()
389 ? INVALID_HANDLE_VALUE : handle, access, bin))
390 api_fatal ("couldn't initialize fd %d for %s", fd, fh->get_name ());
392 cygheap->fdtab[fd] = fh;
394 paranoid_printf ("fd %d, handle %p", fd, handle);
398 #define cnew(name, ...) \
400 void* ptr = (void*) ccalloc (HEAP_FHANDLER, 1, sizeof (name)); \
401 ptr ? new (ptr) name (__VA_ARGS__) : NULL; \
404 #define cnew_no_ctor(name, ...) \
406 void* ptr = (void*) ccalloc (HEAP_FHANDLER, 1, sizeof (name)); \
407 ptr ? new (ptr) name (ptr) : NULL; \
411 build_fh_name (const char *name, unsigned opt, suffix_info *si)
413 path_conv pc (name, opt | PC_NULLEMPTY | PC_POSIX, si);
416 fhandler_base *fh = cnew (fhandler_nodevice);
418 fh->set_error (pc.error);
419 set_errno (fh ? pc.error : EMFILE);
423 return build_fh_pc (pc);
427 build_fh_dev (const device& dev, const char *unix_name)
431 pc.set_normalized_path (unix_name);
433 pc.set_normalized_path (dev.name);
434 return build_fh_pc (pc);
437 #define fh_unset ((fhandler_base *) 1)
439 static fhandler_base *
440 fh_alloc (path_conv& pc)
442 fhandler_base *fh = fh_unset;
443 fhandler_base *fhraw = NULL;
445 switch (pc.dev.get_major ())
448 fh = cnew (fhandler_pty_slave, pc.dev.get_minor ());
451 fh = cnew (fhandler_pty_master, pc.dev.get_minor ());
453 case DEV_CYGDRIVE_MAJOR:
454 fh = cnew (fhandler_cygdrive);
456 case DEV_FLOPPY_MAJOR:
457 case DEV_CDROM_MAJOR:
466 fh = cnew (fhandler_dev_floppy);
469 fh = cnew (fhandler_dev_tape);
471 case DEV_SERIAL_MAJOR:
472 fh = cnew (fhandler_serial);
475 fh = cnew (fhandler_console, pc.dev);
478 switch ((int) pc.dev)
483 fh = cnew (fhandler_console, pc.dev);
487 fh = cnew (fhandler_pty_master, -1);
489 fhraw = cnew_no_ctor (fhandler_pty_master, -1);
492 fh = cnew (fhandler_windows);
495 fh = cnew (fhandler_fifo);
500 fh = cnew (fhandler_pipe);
508 fh = cnew (fhandler_socket);
511 fh = cnew (fhandler_disk_file);
514 fh = cnew (fhandler_dev_null);
518 fh = cnew (fhandler_dev_zero);
522 fh = cnew (fhandler_dev_random);
526 fh = cnew (fhandler_dev_mem);
529 fh = cnew (fhandler_dev_clipboard);
532 fh = cnew (fhandler_dev_dsp);
535 fh = cnew (fhandler_proc);
538 fh = cnew (fhandler_registry);
542 fh = cnew (fhandler_process);
545 fh = cnew (fhandler_procnet);
548 fh = cnew (fhandler_procsys);
551 fh = cnew (fhandler_procsysvipc);
554 fh = cnew (fhandler_netdrive);
558 fhraw = cnew_no_ctor (fhandler_console, -1);
559 else if (myself->ctty <= 0
560 && !myself->set_ctty (fhandler_termios::last, 0))
561 /* no tty assigned */;
562 else if (iscons_dev (myself->ctty))
563 fh = cnew (fhandler_console, pc.dev);
565 fh = cnew (fhandler_pty_slave, myself->ctty);
568 fh = cnew (fhandler_mailslot);
573 /* If `fhraw' is set that means that this fhandler is just a dummy
574 set up for stat(). Mock it up for use by stat without actually
575 trying to do any real initialization. */
580 if (fh->use_archetype ())
584 fh = cnew (fhandler_nodevice);
585 else if (fh->dev () == FH_ERROR)
594 build_fh_pc (path_conv& pc, bool set_name)
596 fhandler_base *fh = fh_alloc (pc);
603 else if (fh->dev () != FH_NADA)
604 fh->set_name (fh->dev ().name);
608 if (!fh->use_archetype ())
609 /* doesn't use archetypes */;
610 else if ((fh->archetype = cygheap->fdtab.find_archetype (fh->dev ())))
611 debug_printf ("found an archetype for %s(%d/%d) io_handle %p", fh->get_name (), fh->dev ().get_major (), fh->dev ().get_minor (),
612 fh->archetype->get_io_handle ());
615 fh->archetype = fh->clone ();
616 debug_printf ("created an archetype (%p) for %s(%d/%d)", fh->archetype, fh->get_name (), fh->dev ().get_major (), fh->dev ().get_minor ());
617 fh->archetype->archetype = NULL;
618 *cygheap->fdtab.add_archetype () = fh->archetype;
621 /* The fhandler_termios constructor keeps track of the last tty-like thing
622 opened but we're only interested in this if we don't have a controlling
623 terminal since we could potentially want to open it if /dev/tty is
625 if (myself->ctty > 0 || !fh->is_tty () || !pc.isctty_capable ())
626 fhandler_termios::last = NULL;
629 debug_printf ("fh %p, dev %p", fh, (DWORD) fh->dev ());
634 dtable::dup_worker (fhandler_base *oldfh, int flags)
636 /* Don't call set_name in build_fh_pc. It will be called in
637 fhandler_base::operator= below. Calling it twice will result
638 in double allocation. */
639 fhandler_base *newfh = oldfh->clone ();
641 debug_printf ("build_fh_pc failed");
644 if (!oldfh->archetype)
645 newfh->set_io_handle (NULL);
647 newfh->pc.reset_conv_handle ();
648 if (oldfh->dup (newfh, flags))
652 debug_printf ("oldfh->dup failed");
657 newfh->archetype_usecount (1);
658 /* The O_CLOEXEC flag enforces close-on-exec behaviour. */
659 newfh->set_close_on_exec (!!(flags & O_CLOEXEC));
660 debug_printf ("duped '%s' old %p, new %p", oldfh->get_name (), oldfh->get_io_handle (), newfh->get_io_handle ());
662 debug_printf ("duped output_handles old %p, new %p",
663 oldfh->get_output_handle (),
664 newfh->get_output_handle ());
665 debug_printf ("duped output_handles archetype old %p, archetype new %p",
666 oldfh->archetype->get_output_handle (),
667 newfh->archetype->get_output_handle ());
675 dtable::dup3 (int oldfd, int newfd, int flags)
678 fhandler_base *newfh = NULL; // = NULL to avoid an incorrect warning
681 debug_printf ("dup3 (%d, %d, %p)", oldfd, newfd, flags);
684 if (not_open (oldfd))
686 syscall_printf ("fd %d not open", oldfd);
692 syscall_printf ("new fd out of bounds: %d", newfd);
696 if ((flags & ~O_CLOEXEC) != 0)
698 syscall_printf ("invalid flags value %x", flags);
703 /* This is a temporary kludge until all utilities can catch up with
704 a change in behavior that implements linux functionality: opening
705 a tty should not automatically cause it to become the controlling
706 tty for the process. */
710 if ((newfh = dup_worker (fds[oldfd], flags)) == NULL)
716 debug_printf ("newfh->io_handle %p, oldfh->io_handle %p, new win32_name %p, old win32_name %p",
717 newfh->get_io_handle (), fds[oldfd]->get_io_handle (), newfh->get_win32_name (), fds[oldfd]->get_win32_name ());
719 if (!not_open (newfd))
721 else if ((size_t) newfd < size)
723 else if (find_unused_handle (newfd) < 0)
732 if ((res = newfd) <= 2)
733 set_std_handle (res);
738 syscall_printf ("%d = dup3 (%d, %d, %p)", res, oldfd, newfd, flags);
744 dtable::select_read (int fd, select_stuff *ss)
751 fhandler_base *fh = fds[fd];
752 select_record *s = fh->select_read (ss);
757 debug_printf ("%s fd %d", fh->get_name (), fd);
762 dtable::select_write (int fd, select_stuff *ss)
769 fhandler_base *fh = fds[fd];
770 select_record *s = fh->select_write (ss);
774 debug_printf ("%s fd %d", fh->get_name (), fd);
779 dtable::select_except (int fd, select_stuff *ss)
786 fhandler_base *fh = fds[fd];
787 select_record *s = fh->select_except (ss);
791 debug_printf ("%s fd %d", fh->get_name (), fd);
796 dtable::move_fd (int from, int to)
798 // close (to); /* It is assumed that this is close-on-exec */
804 dtable::set_file_pointers_for_exec ()
806 /* This is not POSIX-compliant so the function is only called for
807 non-Cygwin processes. */
811 for (size_t i = 0; i < size; i++)
812 if ((fh = fds[i]) != NULL && fh->get_flags () & O_APPEND)
813 SetFilePointer (fh->get_handle (), 0, &off_high, FILE_END);
818 dtable::fixup_after_exec ()
820 first_fd_for_open = 0;
822 for (size_t i = 0; i < size; i++)
823 if ((fh = fds[i]) != NULL)
825 fh->clear_readahead ();
826 fh->fixup_after_exec ();
827 if (fh->close_on_exec ())
831 debug_printf ("closing fd %d since it is an archetype", i);
832 fh->close_with_arch ();
836 else if (fh->get_popen_pid ())
839 SetStdHandle (std_consts[i], fh->get_io_handle ());
841 SetStdHandle (std_consts[i], fh->get_output_handle ());
846 dtable::fixup_after_fork (HANDLE parent)
849 for (size_t i = 0; i < size; i++)
850 if ((fh = fds[i]) != NULL)
852 if (fh->close_on_exec () || fh->need_fork_fixup ())
854 debug_printf ("fd %d (%s)", i, fh->get_name ());
855 fh->fixup_after_fork (parent);
858 SetStdHandle (std_consts[i], fh->get_io_handle ());
860 SetStdHandle (std_consts[i], fh->get_output_handle ());
865 decode_tty (char *buf, WCHAR *w32)
867 int ttyn = wcstol (w32, NULL, 10);
868 __small_sprintf (buf, "/dev/pty%d", ttyn);
871 /* Try to derive posix filename from given handle. Return true if
872 the handle is associated with a cygwin tty. */
874 handle_to_fn (HANDLE h, char *posix_fn)
878 WCHAR *maxmatchdos = NULL;
880 OBJECT_NAME_INFORMATION *ntfn = (OBJECT_NAME_INFORMATION *) tp.w_get ();
882 NTSTATUS status = NtQueryObject (h, ObjectNameInformation, ntfn, 65536, &len);
883 if (!NT_SUCCESS (status))
884 debug_printf ("NtQueryObject failed, %p", status);
885 // NT seems to do this on an unopened file
886 else if (!ntfn->Name.Buffer)
887 debug_printf ("nt->Name.Buffer == NULL");
890 WCHAR *w32 = ntfn->Name.Buffer;
891 size_t w32len = ntfn->Name.Length / sizeof (WCHAR);
894 if (wcscasecmp (w32, DEV_NULL) == 0)
896 strcpy (posix_fn, "/dev/null");
900 if (wcscasecmp (w32, DEV_SOCKET) == 0)
902 strcpy (posix_fn, ":sock:");
906 if (wcsncasecmp (w32, DEV_NAMED_PIPE, DEV_NAMED_PIPE_LEN) == 0)
908 w32 += DEV_NAMED_PIPE_LEN;
909 if (wcsncmp (w32, L"cygwin-", WCLEN (L"cygwin-")) != 0)
911 w32 += WCLEN (L"cygwin-");
912 /* Check for installation key and trailing dash. */
913 w32len = installation_key.Length / sizeof (WCHAR);
914 if (w32len && wcsncmp (w32, installation_key.Buffer, w32len) != 0)
920 bool istty = wcsncmp (w32, L"pty", WCLEN (L"pty")) == 0;
922 decode_tty (posix_fn, w32 + WCLEN (L"pty"));
923 else if (wcsncmp (w32, L"pipe", WCLEN (L"pipe")) == 0)
924 strcpy (posix_fn, "/dev/pipe");
928 WCHAR fnbuf[64 * 1024];
929 if (wcsncasecmp (w32, DEVICE_PREFIX, DEVICE_PREFIX_LEN) != 0
930 || !QueryDosDeviceW (NULL, fnbuf, sizeof (fnbuf)))
932 sys_wcstombs (posix_fn, NT_MAX_PATH, w32, w32len);
936 for (WCHAR *s = fnbuf; *s; s = wcschr (s, '\0') + 1)
938 WCHAR device[NT_MAX_PATH];
939 if (!QueryDosDeviceW (s, device, sizeof (device)))
941 if (wcschr (s, ':') == NULL)
943 WCHAR *q = wcsrchr (device, ';');
946 WCHAR *r = wcschr (q, '\\');
950 int devlen = wcslen (device);
951 if (device[devlen - 1] == L'\\')
952 device[--devlen] = L'\0';
953 if (devlen < maxmatchlen)
955 if (wcsncmp (device, w32, devlen) != 0||
956 (w32[devlen] != L'\0' && w32[devlen] != L'\\'))
958 maxmatchlen = devlen;
960 debug_printf ("current match '%W' = '%W'\n", s, device);
965 WCHAR *p = wcschr (w32 + DEVICE_PREFIX_LEN, L'\\');
966 size_t n = wcslen (maxmatchdos);
972 if (maxmatchdos[n - 1] == L'\\')
974 w32 += maxmatchlen - n;
977 memcpy (w32, maxmatchdos, n * sizeof (WCHAR));
980 else if (wcsncmp (w32, DEV_REMOTE, DEV_REMOTE_LEN) == 0)
982 w32 += DEV_REMOTE_LEN - 2;
984 debug_printf ("remote drive");
986 else if (wcsncmp (w32, DEV_REMOTE1, DEV_REMOTE1_LEN) == 0)
988 w32 += DEV_REMOTE1_LEN - 2;
990 debug_printf ("remote drive");
993 cygwin_conv_path (CCP_WIN_W_TO_POSIX | CCP_ABSOLUTE, w32, posix_fn,
996 debug_printf ("derived path '%W', posix '%s'", w32, posix_fn);
1000 strcpy (posix_fn, unknown_file);
1005 dtable::fixup_before_fork (DWORD target_proc_id)
1009 for (size_t i = 0; i < size; i++)
1010 if ((fh = fds[i]) != NULL)
1012 debug_printf ("fd %d (%s)", i, fh->get_name ());
1013 fh->fixup_before_fork_exec (target_proc_id);
1019 dtable::fixup_before_exec (DWORD target_proc_id)
1023 for (size_t i = 0; i < size; i++)
1024 if ((fh = fds[i]) != NULL && !fh->close_on_exec ())
1026 debug_printf ("fd %d (%s)", i, fh->get_name ());
1027 fh->fixup_before_fork_exec (target_proc_id);