/* File descriptors.
Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
This file is part of the GNU C Library.

The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA.  */

#ifndef	_HURD_FD_H

#define	_HURD_FD_H	1
#include <features.h>

#include <hurd/hurd_types.h>
#include <hurd/port.h>


/* Structure representing a file descriptor.  */

struct hurd_fd
  {
    struct hurd_port port;	/* io server port.  */
    int flags;			/* fcntl flags; locked by port.lock.  */

    /* Normal port to the ctty.  When `port' is our ctty, this is a port to
       the same io object but which never returns EBACKGROUND; when not,
       this is nil.  */
    struct hurd_port ctty;
  };


/* Current file descriptor table.  */

extern int _hurd_dtablesize;
extern struct hurd_fd **_hurd_dtable;
extern struct mutex _hurd_dtable_lock; /* Locks those two variables.  */

#include <hurd/signal.h>
#include <lock-intern.h>

#ifndef _EXTERN_INLINE
#define _EXTERN_INLINE extern __inline
#endif

/* Returns the descriptor cell for FD.  If FD is invalid or unused, return
   NULL.  The cell is unlocked; when ready to use it, lock it and check for
   it being unused.  */

_EXTERN_INLINE struct hurd_fd *
_hurd_fd_get (int fd)
{
  struct hurd_fd *descriptor;

  __mutex_lock (&_hurd_dtable_lock);
  if (fd < 0 || fd >= _hurd_dtablesize)
    descriptor = NULL;
  else
    {
      struct hurd_fd *cell = _hurd_dtable[fd];
      if (cell == NULL)
	/* No descriptor allocated at this index.  */
	descriptor = NULL;
      else
	{
	  __spin_lock (&cell->port.lock);
	  if (cell->port.port == MACH_PORT_NULL)
	    /* The descriptor at this index has no port in it.
	       This happens if it existed before but was closed.  */
	    descriptor = NULL;
	  else
	    descriptor = cell;
	  __spin_unlock (&cell->port.lock);
	}
    }
  __mutex_unlock (&_hurd_dtable_lock);

  return descriptor;
}


/* Evaluate EXPR with the variable `descriptor' bound to a pointer to the
   file descriptor structure for FD.   */

#define	HURD_FD_USE(fd, expr)						      \
  ({ struct hurd_fd *descriptor = _hurd_fd_get (fd);			      \
     descriptor == NULL ? EBADF : (expr); })

/* Evaluate EXPR with the variable `port' bound to the port to FD, and
   `ctty' bound to the ctty port.  */

#define HURD_DPORT_USE(fd, expr) \
  HURD_FD_USE ((fd), HURD_FD_PORT_USE (descriptor, (expr)))

/* Likewise, but FD is a pointer to the file descriptor structure.  */

#define	HURD_FD_PORT_USE(fd, expr)					      \
  ({ error_t __result;							      \
     struct hurd_fd *const __d = (fd);					      \
     struct hurd_userlink __ulink, __ctty_ulink;			      \
     io_t port, ctty;							      \
     void *crit = _hurd_critical_section_lock ();			      \
     __spin_lock (&__d->port.lock);					      \
     if (__d->port.port == MACH_PORT_NULL)				      \
       {								      \
	 __spin_unlock (&__d->port.lock);				      \
	 _hurd_critical_section_unlock (crit);				      \
	 __result = EBADF;						      \
       }								      \
     else								      \
       {								      \
	 ctty = _hurd_port_get (&__d->ctty, &__ctty_ulink);		      \
	 port = _hurd_port_locked_get (&__d->port, &__ulink);		      \
	 _hurd_critical_section_unlock (crit);				      \
	 __result = (expr);						      \
	 _hurd_port_free (&__d->port, &__ulink, port);			      \
	 if (ctty != MACH_PORT_NULL)					      \
	   _hurd_port_free (&__d->ctty, &__ctty_ulink, ctty);		      \
       }								      \
     __result; })

#include <errno.h>

/* Check if ERR should generate a signal.
   Returns the signal to take, or zero if none.  */

_EXTERN_INLINE error_t
_hurd_fd_error_signal (error_t err)
{
  switch (err)
    {
    case EMACH_SEND_INVALID_DEST:
    case EMIG_SERVER_DIED:
      /* The server has disappeared!  */
      return SIGLOST;
    case EPIPE:
      return SIGPIPE;
    default:
      /* Having a default case avoids -Wenum-switch warnings.  */
      return 0;
    }
}

/* Handle an error from an RPC on a file descriptor's port.  You should
   always use this function to handle errors from RPCs made on file
   descriptor ports.  Some errors are translated into signals.  */

_EXTERN_INLINE error_t
_hurd_fd_error (int fd, error_t err)
{
  int signo = _hurd_fd_error_signal (err);
  if (signo)
    {
      const struct hurd_signal_detail detail
	= { code: fd, error: err, exc: 0 };
      _hurd_raise_signal (NULL, signo, &detail);
    }
  return err;
}

/* Handle error code ERR from an RPC on file descriptor FD's port.
   Set `errno' to the appropriate error code, and always return -1.  */

_EXTERN_INLINE int
__hurd_dfail (int fd, error_t err)
{
  errno = _hurd_fd_error (fd, err);
  return -1;
}

/* Set up *FD to have PORT its server port, doing appropriate ctty magic.
   Does no locking or unlocking.  */

extern void _hurd_port2fd (struct hurd_fd *fd, io_t port, int flags);

/* Allocate a new file descriptor and install PORT in it (doing any
   appropriate ctty magic); consumes a user reference on PORT.  FLAGS are
   as for `open'; only O_IGNORE_CTTY is meaningful, but all are saved.

   If the descriptor table is full, set errno, and return -1.
   If DEALLOC is nonzero, deallocate PORT first.  */

extern int _hurd_intern_fd (io_t port, int flags, int dealloc);

/* Allocate a new file descriptor in the table and return it, locked.  The
   new descriptor number will be no less than FIRST_FD.  If the table is
   full, set errno to EMFILE and return NULL.  If FIRST_FD is negative or
   bigger than the size of the table, set errno to EINVAL and return NULL.  */

extern struct hurd_fd *_hurd_alloc_fd (int *fd_ptr, int first_fd);

/* Allocate a new file descriptor structure and initialize its port cells
   with PORT and CTTY.  (This does not affect the descriptor table.)  */

extern struct hurd_fd *_hurd_new_fd (io_t port, io_t ctty);

/* Close a file descriptor, making it available for future reallocation.  */

extern error_t _hurd_fd_close (struct hurd_fd *fd);

/* Read and write data from a file descriptor; just like `read' and `write'.
   If successful, stores the amount actually read or written in *NBYTES.  */

extern error_t _hurd_fd_read (struct hurd_fd *fd, void *buf, size_t *nbytes);
extern error_t _hurd_fd_write (struct hurd_fd *fd,
			       const void *buf, size_t *nbytes);


/* Call *RPC on PORT and/or CTTY; if a call on CTTY returns EBACKGROUND,
   generate SIGTTIN/SIGTTOU or EIO as appropriate.  */

extern error_t _hurd_ctty_input (io_t port, io_t ctty, error_t (*rpc) (io_t));
extern error_t _hurd_ctty_output (io_t port, io_t ctty, error_t (*rpc) (io_t));


#endif	/* hurd/fd.h */
