/* Descriptor management system calls
   Copyright (C) 1991 Free Software Foundation

This file is part of the GNU Hurd.

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

The GNU Hurd 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 General Public License for more details.

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

/* Written by Michael I. Bushnell.  */

#include "mach.h"
#include <cthreads.h>
#include <gnu/posix1_limits.h>
#include <hurd/hurd_types.h>
#include <gnu/errno.h>
#include <gnu/signal.h>
#include <lib/libhurd.h>

#include "io.h"

#include "alix.h"
#include "alix_io.h"
#include <machine/alix_machdep.h>

/* Missing: fcntl, select, fsync */

/* SYSCALL: Get the current size of the descriptor table.  */
int
getdtablesize (void *ap, int *ret1, int *ret2)
{
  *ret1 = dtablesize;
  return 0;
}

/* SYSCALL: Expand the file descriptor table to SIZE.  */
int
setdtablesize (void *ap, int *ret1, int *ret2)
{
  struct
    {
      int size;
    }
  *args = ap;
  struct filed *newtable;
  int i;

  mutex_lock (&iolock);
  
  if (args->size < dtablesize)
    {
      mutex_unlock (&iolock);
      return EINVAL;
    }

  if (args->size == dtablesize)
    {
      mutex_unlock (&iolock);
      return 0;
    }
  
  newtable = realloc (dtable, args->size * sizeof (struct filed));
  if (newtable)
    {
      dtable = newtable;
      for (i = dtablesize; i < args->size; i++)
	{
	  dtable[i].ioserver = 0;
	}
      dtablesize = args->size;
      mutex_unlock (&iolock);
      return 0;
    }
  else
    {
      mutex_unlock (&iolock);
      return ENOMEM;
    }
}

/* SYSCALL: Get the IO port corresponding to open file descriptor D.  */
int
getdport (void *ap, int *ret1, int *ret2)
{
  struct
    {
      int d;
    }
  *args = ap;
  io_t port;

  mutex_lock (&iolock);
  if (args->d < 0 || args->d >= dtablesize || !dtable[args->d].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }

  port = dtable[args->d].ioserver;
  mach_port_mod_refs (mach_task_self (), port, MACH_PORT_RIGHT_SEND, 1);
  mutex_unlock (&iolock);
  *ret1 = port;
  return 0;
}

/* SYSCALL: Create a new file descriptor for PORT.  */
int
portopen (void *ap, int *ret1, int *ret2)
{
  struct
    {
      io_t port;
    }
  *args = ap;
  int new;
  
  mutex_lock (&iolock);
  new = falloc ();
  if (new == -1)
    {
      mutex_unlock (&iolock);
      return EMFILE;
    }
  mach_port_mod_refs (mach_task_self (), args->port, MACH_PORT_RIGHT_SEND, 1);
  dtable[new].ioserver = args->port;
  mutex_unlock (&iolock);
  *ret1 = new;
  return 0;
}

/* SYSCALL: Create a new file descriptor referencing OLD.  */
int
dup (void *ap, int *ret1, int *ret2)
{
  struct
    {
      int old;
    }
  *args = ap;
  int new;

  mutex_lock (&iolock);
  
  if (args->old < 0 || args->old >= dtablesize || !dtable[args->old].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }

  new = falloc ();
  
  if (new == -1)
    {
      mutex_unlock (&iolock);
      return EMFILE;
    }

  /* We have both descriptors locked and valid, and can proceed.  */
  dtable[new].ioserver = dtable[args->old].ioserver;
  mach_port_mod_refs (mach_task_self (), dtable[new].ioserver, 
		      MACH_PORT_RIGHT_SEND, 1);

  mutex_unlock (&iolock);
  *ret1 = new;
  return 0;
}

/* Close the locked descriptor FILED.  */
static void
closef (int filed)
{
  mach_port_deallocate (mach_task_self (), dtable[filed].ioserver);
  dtable[filed].ioserver = MACH_PORT_NULL;
}

/* SYSCALL: Make NEW be a descriptor referencing OLD.  Close NEW if
   necessary.  */
int
dup2 (void *ap, int *ret1, int *ret2)
{
  struct
    {
      int old;
      int new;
    }
  *args = ap;

  mutex_lock (&iolock);

  if (args->old < 0 || args->old >= dtablesize
      || args->new < 0 || args->new >= dtablesize
      || !dtable[args->old].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }
  
  if (dtable[args->new].ioserver)
    closef (args->new);
  
  dtable[args->new].ioserver = dtable[args->old].ioserver;
  mach_port_mod_refs (mach_task_self (), dtable[args->new].ioserver, 
		      MACH_PORT_RIGHT_SEND, 1);
  mutex_unlock (&iolock);
  return 0;
}

/* SYSCALL: Close a file descriptor F.  */
int
close (void *ap, int *ret1, int *ret2)
{
  struct
    {
      int f;
    }
  *args = ap;

  mutex_lock (&iolock);
  
  if (args->f < 0 || args->f >= dtablesize || !dtable[args->f].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }
  
  closef (args->f);

  mutex_unlock (&iolock);
  return 0;
}

/* Return the index of an available file descriptor, or -1 if the
   table is full.  */
int
falloc ()
{
  int i;
  for (i = 0; i < dtablesize; i++)
    if (!dtable[i].ioserver)
      {
	dtable[i].isctty = -1;
	return i;
      }
  return -1;
}

/* Return true if D is a file descriptor to our control terminal.  */
int
ctty_check (d)
     int d;
{
  if (dtable[d].isctty == -1 && hasctty)
    {
      struct io_statbuf stb;
      error_t err;
      err = io_stat (dtable[d].ioserver, &stb);
      dtable[d].isctty = (!err && stb.stb_fstype == ctty_fstype
			  && stb.stb_fsid.val[0] == ctty_fsid.val[0]
			  && stb.stb_fsid.val[1] == ctty_fsid.val[1]
			  && stb.stb_file_id == ctty_fileid);
    }
  
  return hasctty && dtable[d].isctty;
}

