601 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			601 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* hinfo.cc: file descriptor support.
 | |
| 
 | |
|    Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions.
 | |
| 
 | |
| This file is part of Cygwin.
 | |
| 
 | |
| This software is a copyrighted work licensed under the terms of the
 | |
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
| details. */
 | |
| 
 | |
| #define  __INSIDE_CYGWIN_NET__
 | |
| 
 | |
| #include <errno.h>
 | |
| #include <sys/socket.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| #include <ctype.h>
 | |
| #include <unistd.h>
 | |
| #include <fcntl.h>
 | |
| 
 | |
| #define Win32_Winsock
 | |
| #include "winsup.h"
 | |
| 
 | |
| hinfo dtable;
 | |
| 
 | |
| /* Set aside space for the table of fds */
 | |
| void
 | |
| dtable_init (void)
 | |
| {
 | |
|   if (!dtable.size)
 | |
|     dtable.extend(NOFILE_INCR);
 | |
| }
 | |
| 
 | |
| void __stdcall
 | |
| set_std_handle (int fd)
 | |
| {
 | |
|   if (fd == 0)
 | |
|     SetStdHandle (STD_INPUT_HANDLE, dtable[fd]->get_handle ());
 | |
|   else if (fd == 1)
 | |
|     SetStdHandle (STD_OUTPUT_HANDLE, dtable[fd]->get_output_handle ());
 | |
|   else if (fd == 2)
 | |
|     SetStdHandle (STD_ERROR_HANDLE, dtable[fd]->get_output_handle ());
 | |
| }
 | |
| 
 | |
| int
 | |
| hinfo::extend (int howmuch)
 | |
| {
 | |
|   int new_size = size + howmuch;
 | |
|   fhandler_base **newfds;
 | |
| 
 | |
|   if (howmuch <= 0)
 | |
|     return 0;
 | |
| 
 | |
|   /* Try to allocate more space for fd table. We can't call realloc()
 | |
|      here to preserve old table if memory allocation fails */
 | |
| 
 | |
|   if (!(newfds = (fhandler_base **) calloc (new_size, sizeof newfds[0])))
 | |
|     {
 | |
|       debug_printf ("calloc failed");
 | |
|       return 0;
 | |
|     }
 | |
|   if (fds)
 | |
|     {
 | |
|       memcpy (newfds, fds, size * sizeof (fds[0]));
 | |
|       free (fds);
 | |
|     }
 | |
| 
 | |
|   size = new_size;
 | |
|   fds = newfds;
 | |
|   debug_printf ("size %d, fds %p", size, fds);
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| /* Initialize the file descriptor/handle mapping table.
 | |
|    We only initialize the parent table here.  The child table is
 | |
|    initialized at each fork () call.  */
 | |
| 
 | |
| void
 | |
| hinfo_init (void)
 | |
| {
 | |
|   /* Set these before trying to output anything from strace.
 | |
|      Also, always set them even if we're to pick up our parent's fds
 | |
|      in case they're missed.  */
 | |
| 
 | |
|   if (!parent_alive && NOTSTATE(myself, PID_CYGPARENT))
 | |
|     {
 | |
|       HANDLE in = GetStdHandle (STD_INPUT_HANDLE);
 | |
|       HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
 | |
|       HANDLE err = GetStdHandle (STD_ERROR_HANDLE);
 | |
| 
 | |
|       dtable.init_std_file_from_handle (0, in, GENERIC_READ, "{stdin}");
 | |
| 
 | |
|       /* STD_ERROR_HANDLE has been observed to be the same as
 | |
| 	 STD_OUTPUT_HANDLE.  We need separate handles (e.g. using pipes
 | |
| 	 to pass data from child to parent).  */
 | |
|       if (out == err)
 | |
| 	{
 | |
| 	  /* Since this code is not invoked for forked tasks, we don't have
 | |
| 	     to worry about the close-on-exec flag here.  */
 | |
| 	  if (!DuplicateHandle (hMainProc, out, hMainProc, &err, 0,
 | |
| 				 1, DUPLICATE_SAME_ACCESS))
 | |
| 	    {
 | |
| 	      /* If that fails, do this as a fall back.  */
 | |
| 	      err = out;
 | |
| 	      system_printf ("couldn't make stderr distinct from stdout");
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       dtable.init_std_file_from_handle (1, out, GENERIC_WRITE, "{stdout}");
 | |
|       dtable.init_std_file_from_handle (2, err, GENERIC_WRITE, "{stderr}");
 | |
|     }
 | |
| }
 | |
| 
 | |
| int
 | |
| hinfo::not_open (int fd)
 | |
| {
 | |
|   SetResourceLock(LOCK_FD_LIST,READ_LOCK," not_open");
 | |
| 
 | |
|   int res = fd < 0 || fd >= (int)size || fds[fd] == NULL;
 | |
| 
 | |
|   ReleaseResourceLock(LOCK_FD_LIST,READ_LOCK," not open");
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| int
 | |
| hinfo::find_unused_handle (int start)
 | |
| {
 | |
|   AssertResourceOwner(LOCK_FD_LIST,READ_LOCK);
 | |
| 
 | |
|   do
 | |
|     {
 | |
|       for (int i = start; i < (int) size; i++)
 | |
| 	if (not_open (i))
 | |
| 	  return i;
 | |
|     }
 | |
|   while (extend (NOFILE_INCR));
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| void
 | |
| hinfo::release (int fd)
 | |
| {
 | |
|   if (!not_open (fd))
 | |
|     {
 | |
|       delete (fds[fd]);
 | |
|       fds[fd] = NULL;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| hinfo::init_std_file_from_handle (int fd, HANDLE handle,
 | |
| 				  DWORD myaccess, const char *name)
 | |
| {
 | |
|   int bin = __fmode;
 | |
|   /* Check to see if we're being redirected - if not then
 | |
|      we open then as consoles */
 | |
|   if (fd == 0 || fd == 1 || fd == 2)
 | |
|     {
 | |
|       first_fd_for_open = 0;
 | |
|       /* See if we can consoleify it  - if it is a console,
 | |
|        don't open it in binary.  That will screw up our crlfs*/
 | |
|       CONSOLE_SCREEN_BUFFER_INFO buf;
 | |
|       if (GetConsoleScreenBufferInfo (handle, &buf))
 | |
| 	{
 | |
| 	  bin = 0;
 | |
| 	  if (ISSTATE (myself, PID_USETTY))
 | |
| 	    name = "/dev/tty";
 | |
| 	  else
 | |
| 	    name = "/dev/conout";
 | |
| 	}
 | |
|       else if (FlushConsoleInputBuffer (handle))
 | |
| 	{
 | |
| 	  bin = 0;
 | |
| 	  if (ISSTATE (myself, PID_USETTY))
 | |
| 	    name = "/dev/tty";
 | |
| 	  else
 | |
| 	    name = "/dev/conin";
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   build_fhandler (fd, name, handle)->init (handle, myaccess, bin);
 | |
|   set_std_handle (fd);
 | |
|   paranoid_printf ("fd %d, handle %p", fd, handle);
 | |
| }
 | |
| 
 | |
| extern "C"
 | |
| int
 | |
| cygwin_attach_handle_to_fd (char *name, int fd, HANDLE handle, mode_t bin,
 | |
| 			      DWORD myaccess)
 | |
| {
 | |
|   if (fd == -1)
 | |
|     fd = dtable.find_unused_handle();
 | |
|   fhandler_base *res = dtable.build_fhandler (fd, name, handle);
 | |
|   res->init (handle, myaccess, bin);
 | |
|   return fd;
 | |
| }
 | |
| 
 | |
| fhandler_base *
 | |
| hinfo::build_fhandler (int fd, const char *name, HANDLE handle)
 | |
| {
 | |
|   int unit;
 | |
|   DWORD devn;
 | |
| 
 | |
|   if ((devn = get_device_number (name, unit)) == FH_BAD)
 | |
|     {
 | |
|       struct sockaddr sa;
 | |
|       int sal = sizeof (sa);
 | |
|       CONSOLE_SCREEN_BUFFER_INFO cinfo;
 | |
|       DCB dcb;
 | |
| 
 | |
|       if (handle == NULL)
 | |
| 	devn = FH_DISK;
 | |
|       else if (GetNumberOfConsoleInputEvents (handle, (DWORD *) &cinfo))
 | |
| 	devn = FH_CONIN;
 | |
|       else if (GetConsoleScreenBufferInfo (handle, &cinfo))
 | |
| 	devn= FH_CONOUT;
 | |
|       else if (wsock32_handle && getpeername ((SOCKET) handle, &sa, &sal))
 | |
| 	devn = FH_SOCKET;
 | |
|       else if (GetFileType (handle) == FILE_TYPE_PIPE)
 | |
| 	devn = FH_PIPE;
 | |
|       else if (GetCommState (handle, &dcb))
 | |
| 	devn = FH_SERIAL;
 | |
|       else
 | |
| 	devn = FH_DISK;
 | |
|     }
 | |
| 
 | |
|   return build_fhandler (fd, devn, name, unit);
 | |
| }
 | |
| 
 | |
| fhandler_base *
 | |
| hinfo::build_fhandler (int fd, DWORD dev, const char *name, int unit)
 | |
| {
 | |
|   fhandler_base *fh;
 | |
|   void *buf = calloc (1, sizeof (fhandler_union) + 100);
 | |
| 
 | |
|   dev &= FH_DEVMASK;
 | |
|   switch (dev)
 | |
|     {
 | |
|       case FH_TTYM:
 | |
| 	fh = new (buf) fhandler_tty_master (name, unit);
 | |
| 	break;
 | |
|       case FH_CONSOLE:
 | |
|       case FH_CONIN:
 | |
|       case FH_CONOUT:
 | |
| 	fh = new (buf) fhandler_console (name);
 | |
| 	break;
 | |
|       case FH_PTYM:
 | |
| 	fh = new (buf) fhandler_pty_master (name);
 | |
| 	break;
 | |
|       case FH_TTYS:
 | |
| 	if (unit < 0)
 | |
| 	  fh = new (buf) fhandler_tty_slave (name);
 | |
| 	else
 | |
| 	  fh = new (buf) fhandler_tty_slave (unit, name);
 | |
| 	break;
 | |
|       case FH_WINDOWS:
 | |
| 	fh = new (buf) fhandler_windows (name);
 | |
| 	break;
 | |
|       case FH_SERIAL:
 | |
| 	fh = new (buf) fhandler_serial (name, dev, unit);
 | |
| 	break;
 | |
|       case FH_PIPE:
 | |
|       case FH_PIPER:
 | |
|       case FH_PIPEW:
 | |
| 	fh = new (buf) fhandler_pipe (name, dev);
 | |
| 	break;
 | |
|       case FH_SOCKET:
 | |
| 	fh = new (buf) fhandler_socket (name);
 | |
| 	break;
 | |
|       case FH_DISK:
 | |
| 	fh = new (buf) fhandler_disk_file (NULL);
 | |
| 	break;
 | |
|       case FH_FLOPPY:
 | |
| 	fh = new (buf) fhandler_dev_floppy (name, unit);
 | |
| 	break;
 | |
|       case FH_TAPE:
 | |
| 	fh = new (buf) fhandler_dev_tape (name, unit);
 | |
| 	break;
 | |
|       case FH_NULL:
 | |
| 	fh = new (buf) fhandler_dev_null (name);
 | |
| 	break;
 | |
|       case FH_ZERO:
 | |
| 	fh = new (buf) fhandler_dev_zero (name);
 | |
| 	break;
 | |
|       default:
 | |
| 	/* FIXME - this could recurse forever */
 | |
| 	return build_fhandler (fd, name, NULL);
 | |
|     }
 | |
| 
 | |
|   debug_printf ("%s - cb %d, fd %d, fh %p", fh->get_name () ?: "", fh->cb,
 | |
| 		fd, fh);
 | |
|   return fd >= 0 ? (fds[fd] = fh) : fh;
 | |
| }
 | |
| 
 | |
| fhandler_base *
 | |
| hinfo::dup_worker (fhandler_base *oldfh)
 | |
| {
 | |
|   fhandler_base *newfh = build_fhandler (-1, oldfh->get_device (), NULL);
 | |
|   *newfh = *oldfh;
 | |
|   newfh->set_io_handle (NULL);
 | |
|   if (oldfh->dup (newfh))
 | |
|     {
 | |
|       free (newfh);
 | |
|       newfh = NULL;
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   newfh->set_close_on_exec_flag (0);
 | |
|   MALLOC_CHECK;
 | |
|   return newfh;
 | |
| }
 | |
| 
 | |
| int
 | |
| hinfo::dup2 (int oldfd, int newfd)
 | |
| {
 | |
|   int res = -1;
 | |
|   fhandler_base *newfh = NULL;	// = NULL to avoid an incorrect warning
 | |
| 
 | |
|   MALLOC_CHECK;
 | |
|   debug_printf ("dup2 (%d, %d)", oldfd, newfd);
 | |
| 
 | |
|   if (not_open (oldfd))
 | |
|     {
 | |
|       syscall_printf("dup2: fd %d not open", oldfd);
 | |
|       set_errno (EBADF);
 | |
|       goto done;
 | |
|     }
 | |
| 
 | |
|   if (newfd == oldfd)
 | |
|     {
 | |
|       res = 0;
 | |
|       goto done;
 | |
|     }
 | |
| 
 | |
|   if ((newfh = dup_worker (fds[oldfd])) == NULL)
 | |
|     {
 | |
|       res = -1;
 | |
|       goto done;
 | |
|     }
 | |
| 
 | |
|   SetResourceLock(LOCK_FD_LIST,WRITE_LOCK|READ_LOCK,"dup");
 | |
|   if (!not_open (newfd))
 | |
|     _close (newfd);
 | |
|   fds[newfd] = newfh;
 | |
|   ReleaseResourceLock(LOCK_FD_LIST,WRITE_LOCK|READ_LOCK,"dup");
 | |
|   MALLOC_CHECK;
 | |
| 
 | |
|   if ((res = newfd) <= 2)
 | |
|     set_std_handle (res);
 | |
| 
 | |
|   MALLOC_CHECK;
 | |
| done:
 | |
|   syscall_printf ("%d = dup2 (%d, %d)", res, oldfd, newfd);
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| select_record *
 | |
| hinfo::select_read (int fd, select_record *s)
 | |
| {
 | |
|   if (dtable.not_open (fd))
 | |
|     {
 | |
|       set_errno (EBADF);
 | |
|       return NULL;
 | |
|     }
 | |
|   fhandler_base *fh = dtable[fd];
 | |
|   s = fh->select_read (s);
 | |
|   s->fd = fd;
 | |
|   s->fh = fh;
 | |
|   s->saw_error = 0;
 | |
|   debug_printf ("%s fd %d", fh->get_name (), fd);
 | |
|   return s;
 | |
| }
 | |
| 
 | |
| select_record *
 | |
| hinfo::select_write (int fd, select_record *s)
 | |
| {
 | |
|   if (dtable.not_open (fd))
 | |
|     {
 | |
|       set_errno (EBADF);
 | |
|       return NULL;
 | |
|     }
 | |
|   fhandler_base *fh = dtable[fd];
 | |
|   s = fh->select_write (s);
 | |
|   s->fd = fd;
 | |
|   s->fh = fh;
 | |
|   s->saw_error = 0;
 | |
|   debug_printf ("%s fd %d", fh->get_name (), fd);
 | |
|   return s;
 | |
| }
 | |
| 
 | |
| select_record *
 | |
| hinfo::select_except (int fd, select_record *s)
 | |
| {
 | |
|   if (dtable.not_open (fd))
 | |
|     {
 | |
|       set_errno (EBADF);
 | |
|       return NULL;
 | |
|     }
 | |
|   fhandler_base *fh = dtable[fd];
 | |
|   s = fh->select_except (s);
 | |
|   s->fd = fd;
 | |
|   s->fh = fh;
 | |
|   s->saw_error = 0;
 | |
|   debug_printf ("%s fd %d", fh->get_name (), fd);
 | |
|   return s;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Function to take an existant hinfo array
 | |
|  * and linearize it into a memory buffer.
 | |
|  * If memory buffer is NULL, it returns the size
 | |
|  * of memory buffer needed to do the linearization.
 | |
|  * On error returns -1.
 | |
|  */
 | |
| 
 | |
| int
 | |
| hinfo::linearize_fd_array (unsigned char *in_buf, int buflen)
 | |
| {
 | |
|   /* If buf == NULL, just precalculate length */
 | |
|   if (in_buf == NULL)
 | |
|     {
 | |
|       buflen = sizeof (size_t);
 | |
|       for (int i = 0, max_used_fd = -1; i < (int)size; i++)
 | |
| 	if (!not_open (i) && !fds[i]->get_close_on_exec ())
 | |
| 	  {
 | |
| 	    buflen += i - (max_used_fd + 1);
 | |
| 	    buflen += fds[i]->cb + strlen (fds[i]->get_name ()) + 1
 | |
| 				 + strlen (fds[i]->get_win32_name ()) + 1;
 | |
| 	    max_used_fd = i;
 | |
| 	  }
 | |
|       debug_printf ("needed buflen %d", buflen);
 | |
|       return buflen;
 | |
|     }
 | |
| 
 | |
|   debug_printf ("in_buf = %x, buflen = %d", in_buf, buflen);
 | |
| 
 | |
|   /*
 | |
|    * Now linearize each open fd (write a 0xff byte for a closed fd).
 | |
|    * Write the name of the open fd first (null terminated). This
 | |
|    * allows the de_linearizeing code to determine what kind of fhandler_xxx
 | |
|    * to create.
 | |
|    */
 | |
| 
 | |
|   size_t i;
 | |
|   int len, total_size;
 | |
| 
 | |
|   total_size = sizeof (size_t);
 | |
|   if (total_size > buflen)
 | |
|     {
 | |
|       system_printf ("FATAL: linearize_fd_array exceeded buffer size");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   unsigned char *buf = in_buf;
 | |
|   buf += sizeof (size_t);	/* skip over length which is added later */
 | |
| 
 | |
|   for (i = 0, total_size = sizeof (size_t); total_size < buflen; i++)
 | |
|     {
 | |
|       if (not_open (i) || fds[i]->get_close_on_exec ())
 | |
| 	{
 | |
| 	  debug_printf ("linearizing closed fd %d",i);
 | |
| 	  *buf = 0xff;		/* place holder */
 | |
| 	  len = 1;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  len = fds[i]->linearize (buf);
 | |
| 	  debug_printf ("fd %d, len %d, name %s, device %p", i, len, buf,
 | |
| 			fds[i]->get_device ());
 | |
| 	}
 | |
| 
 | |
|       total_size += len;
 | |
|       buf += len;
 | |
|     }
 | |
| 
 | |
|   i--;
 | |
|   memcpy (in_buf, &i, sizeof (size_t));
 | |
|   if (total_size != buflen)
 | |
|     system_printf ("out of sync %d != %d", total_size, buflen);
 | |
|   return total_size;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Function to take a linearized hinfo array in a memory buffer and
 | |
|  * re-create the original hinfo array.
 | |
|  */
 | |
| 
 | |
| LPBYTE
 | |
| hinfo::de_linearize_fd_array (LPBYTE buf)
 | |
| {
 | |
|   int len;
 | |
|   size_t max_used_fd, inc_size;
 | |
| 
 | |
|   debug_printf ("buf %x", buf);
 | |
| 
 | |
|   /* First get the number of fd's - use this to set the dtablesize.
 | |
|      NB. This is the only place in the code this should be done !!
 | |
|   */
 | |
| 
 | |
|   memcpy ((char *) &max_used_fd, buf, sizeof (int));
 | |
|   buf += sizeof (size_t);
 | |
| 
 | |
|   inc_size = NOFILE_INCR * ((max_used_fd + NOFILE_INCR - 1) / NOFILE_INCR) -
 | |
| 	     size;
 | |
|   debug_printf ("max_used_fd %d, inc size %d", max_used_fd, inc_size);
 | |
|   if (inc_size > 0 && !extend (inc_size))
 | |
|     {
 | |
|       system_printf ("out of memory");
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   for (size_t i = 0; i <= max_used_fd; i++)
 | |
|     {
 | |
|       /* 0xFF means closed */
 | |
|       if (*buf == 0xff)
 | |
| 	{
 | |
| 	  fds[i] = NULL;
 | |
| 	  buf++;
 | |
| 	  debug_printf ("closed fd %d", i);
 | |
| 	  continue;
 | |
| 	}
 | |
|       /* fd was open - de_linearize it */
 | |
|       /* Get the null-terminated name.  It is followed by an image of
 | |
| 	 the actual fhandler_* structure.  Use the status field from
 | |
| 	 this to build a new fhandler type. */
 | |
| 
 | |
|       DWORD status;
 | |
|       LPBYTE obuf = buf;
 | |
|       char *win32;
 | |
|       win32 = strchr ((char *)obuf, '\0') + 1;
 | |
|       buf = (LPBYTE)strchr ((char *)win32, '\0') + 1;
 | |
|       memcpy ((char *)&status, buf + FHSTATOFF, sizeof(DWORD));
 | |
|       debug_printf ("fd %d, name %s, win32 name %s, status %p",
 | |
| 		    i, obuf, win32, status);
 | |
|       len = build_fhandler (i, status, (const char *) NULL)->
 | |
| 	    de_linearize ((char *) buf, (char *) obuf, win32);
 | |
|       set_std_handle (i);
 | |
|       buf += len;
 | |
|       debug_printf ("len %d", buf - obuf);
 | |
|     }
 | |
|   first_fd_for_open = 0;
 | |
|   return buf;
 | |
| }
 | |
| 
 | |
| void
 | |
| hinfo::fixup_after_fork (HANDLE parent)
 | |
| {
 | |
|   SetResourceLock(LOCK_FD_LIST,WRITE_LOCK|READ_LOCK,"dup");
 | |
|   for (size_t i = 0; i < size; i++)
 | |
|     if (!not_open (i))
 | |
|       {
 | |
| 	fhandler_base *fh = fds[i];
 | |
| 	if (fh->get_close_on_exec () || fh->get_need_fork_fixup ())
 | |
| 	  {
 | |
| 	    debug_printf ("fd %d(%s)", i, fh->get_name ());
 | |
| 	    fh->fixup_after_fork (parent);
 | |
| 	  }
 | |
|       }
 | |
|   ReleaseResourceLock(LOCK_FD_LIST,WRITE_LOCK|READ_LOCK,"dup");
 | |
| }
 | |
| 
 | |
| int
 | |
| hinfo::vfork_child_dup ()
 | |
| {
 | |
|   fhandler_base **newtable;
 | |
|   newtable = (fhandler_base **) calloc (size, sizeof(fds[0]));
 | |
|   int res = 1;
 | |
| 
 | |
|   SetResourceLock(LOCK_FD_LIST,WRITE_LOCK|READ_LOCK,"dup");
 | |
|   for (size_t i = 0; i < size; i++)
 | |
|     if (not_open (i))
 | |
|       continue;
 | |
|     else if ((newtable[i] = dup_worker (fds[i])) == NULL)
 | |
|       {
 | |
| 	res = 0;
 | |
| 	set_errno (EBADF);
 | |
| 	goto out;
 | |
|       }
 | |
|   fds_on_hold = fds;
 | |
|   fds = newtable;
 | |
| out:
 | |
|   ReleaseResourceLock(LOCK_FD_LIST,WRITE_LOCK|READ_LOCK,"dup");
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| void
 | |
| hinfo::vfork_parent_restore ()
 | |
| {
 | |
|   SetResourceLock(LOCK_FD_LIST,WRITE_LOCK|READ_LOCK,"dup");
 | |
| 
 | |
|   close_all_files ();
 | |
|   fhandler_base **deleteme = fds;
 | |
|   fds = fds_on_hold;
 | |
|   fds_on_hold = NULL;
 | |
|   free (deleteme);
 | |
| 
 | |
|   ReleaseResourceLock(LOCK_FD_LIST,WRITE_LOCK|READ_LOCK,"dup");
 | |
|   return;
 | |
| }
 |