/* 
   Copyright (C) 1994 Free Software Foundation

   This program 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 2, or (at
   your option) any later version.

   This program 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 this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

#include <hurd.h>
#include <hurd/ports.h>
#include <hurd/trivfs.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <hurd/fsys.h>
#include <hurd/msg.h>

#include "socket_S.h"
#include "interrupt_S.h"
#include "notify_S.h"
#include "io_S.h"
#include "io_reply_U.h"

/* This is a kludgy server for PF_LOCAL that implements only enough
   for the libc pipe() routine to work. */

struct pipe_user
{
  struct port_info pi;
  struct pipe_end *pe;
};

struct pipe_end
{
  int refcount;
  enum { PIPE_NONE, PIPE_READ, PIPE_WRITE } type;
  struct pipe_end *peer;
  struct pipe *data;
};

struct pipe
{
  char *start, *end;
  size_t chars_alloced;
  struct stat *st;
  struct read_queue *rq_head, *rq_tail;
  struct select_req *pending_selects;
  char data[0];
};

struct read_queue
{
  mach_port_t reply;
  mach_msg_type_name_t replytype;
  mach_port_t reqport;
  int amount;
  struct read_queue *next;
};

/* Pending select operation */
struct select_req
{
  mach_port_t reply;
  mach_msg_type_name_t replytype;
  int idtag;
  struct select_req *next;
};

/* libports port types. */
#define PT_TRIVFS_CNTL 0	/* translator control port */
#define PT_TRIVFS_PROTID 1	/* node port for translated node */
#define PT_SOCKET 2		/* socket port */

#define INIT_PIPE_BUF 512


/* Forward declarations */
void pipe_user_clean (void *);
void update_time (time_t *, u_long *);

int
main (int argc, char **argv)
{
  mach_port_t file;
  error_t err;
  mach_port_t bootstrap;
  
  _libports_initialize ();	/* XXX */

  task_get_bootstrap_port (mach_task_self (), &bootstrap);
  
  if (bootstrap == MACH_PORT_NULL)
    {
      if (argc != 2)
	{
	  fprintf (stderr, "Usage: %s transname\n", argv[0]);
	  exit (1);
	}
      
      file = file_name_lookup (argv[1], O_CREAT|O_NOTRANS, 0666);
      if (file == MACH_PORT_NULL)
	{
	  perror (argv[1]);
	  exit (1);
	}
      
      err = file_set_translator (file, 0, FS_TRANS_EXCL|FS_TRANS_SET, 0, 0, 0, 
				 trivfs_handle_port (file, PT_TRIVFS_CNTL, 
						     PT_TRIVFS_PROTID),
				 MACH_MSG_TYPE_MAKE_SEND);
      if (err)
	{
	  errno = err;
	  perror ("Setting translator");
	  exit (1);
	}
    }
  else
    {
      mach_port_t ctlport;
      struct trivfs_control *pi;
      
      if (argc != 1)
	{
	  fprintf (stderr, "Translator usage: %s\n", argv[0]);
	  exit (1);
	}
      
      ctlport = trivfs_handle_port (MACH_PORT_NULL, PT_TRIVFS_CNTL, 
				    PT_TRIVFS_PROTID);
      errno = fsys_startup (bootstrap, 
			    ctlport, MACH_MSG_TYPE_MAKE_SEND, &file);
      if (errno)
	{
	  perror ("Translator startup");
	  exit (1);
	}

      pi = ports_get_port (ctlport);
      assert (pi);
      pi->underlying = file;
      ports_done_with_port (pi);
    }
  
  ports_manage_port_operations_onethread ();
  return 0;
}

/* Interfaces for trivfs on rendezvous port */
int trivfs_fstype = FSTYPE_MISC;
int trivfs_fsid = 0;
int trivfs_support_read = 0;
int trivfs_support_write = 0;
int trivfs_support_exec = 0;
int trivfs_allow_open = 0;

int trivfs_protid_porttypes[] = {PT_TRIVFS_PROTID};
int trivfs_cntl_porttypes[] = {PT_TRIVFS_CNTL};
int trivfs_protid_nporttypes = 1;
int trivfs_cntl_nporttypes = 1;

void
trivfs_modify_stat (struct stat *st)
{
}

error_t
trivfs_goaway (int flags,
	       mach_port_t realnode,
	       int cntltype,
	       int protidtype)
{
  return EBUSY;
}

/* Interfaces with ports library */
void (*ports_cleanroutines[])(void *) =
{
  [PT_TRIVFS_PROTID] = trivfs_clean_protid,
  [PT_TRIVFS_CNTL] = trivfs_clean_cntl,
  [PT_SOCKET] = pipe_user_clean,
};

void 
ports_no_live_ports ()
{
}

void
ports_no_hard_ports ()
{
}

void
ports_notice_idle (int nhard, int nsoft)
{
}
     

#if 0
mach_port_status_t mps;
int send_rights;
int receive_rights;
int send_once_rights;
int port_set_rights;
int dead_rights;

void
port_status (mach_port_t port)
{
  mach_port_get_receive_status (mach_task_self (), port, &mps);
  mach_port_get_refs (mach_task_self (), port, MACH_PORT_RIGHT_SEND,
		      &send_rights);
  mach_port_get_refs (mach_task_self (), port, MACH_PORT_RIGHT_RECEIVE,
		      &receive_rights);
  mach_port_get_refs (mach_task_self (), port, MACH_PORT_RIGHT_SEND_ONCE,
		      &send_once_rights);
  mach_port_get_refs (mach_task_self (), port, MACH_PORT_RIGHT_PORT_SET,
		      &port_set_rights);
  mach_port_get_refs (mach_task_self (), port, MACH_PORT_RIGHT_DEAD_NAME,
		      &dead_rights);
}
#endif


int
ports_demuxer (mach_msg_header_t *inp,
	       mach_msg_header_t *outp)
{
  struct port_info *pi;
  int ret;
  extern int socket_server (mach_msg_header_t *, mach_msg_header_t *);
  extern int io_server (mach_msg_header_t *, mach_msg_header_t *);
  extern int interrupt_server (mach_msg_header_t *, mach_msg_header_t *);
  extern int notify_server (mach_msg_header_t *, mach_msg_header_t *);
  
  pi = ports_get_port (inp->msgh_local_port);
  
  if (pi->type == PT_TRIVFS_CNTL
      || pi->type == PT_TRIVFS_PROTID)
    ret = (trivfs_demuxer (inp, outp)
	   || socket_server (inp, outp));
  else
    ret = (io_server (inp, outp)
	   || socket_server (inp, outp)
	   || interrupt_server (inp, outp)
	   || notify_server (inp, outp));
  ports_done_with_port (pi);
  return ret;
}

/* Notify select requestors.  */
void
notify_selects (struct select_req **pending_selects)
{
  struct select_req *sel = *pending_selects;
  while (sel)
    {
      struct select_req *next = sel->next;
      io_select_done (sel->reply, sel->replytype, /* XXX blocks */
		      SELECT_READ, sel->idtag);
      free (sel);
      sel = next;
    }
  *pending_selects = 0;
}


/* I/O interface definitions. */
kern_return_t
S_io_write (mach_port_t reqport,
	    mach_port_t reply,
	    mach_msg_type_name_t replytype,
	    char *data,
	    mach_msg_type_number_t datalen,
	    off_t offset,
	    mach_msg_type_number_t *amount)
{
  struct pipe *p;
  struct pipe_end *pe;
  struct pipe_user *user = ports_check_port_type (reqport, PT_SOCKET);
  
  if (!user)
    return EOPNOTSUPP;
  
  pe = user->pe;

  if (pe->type != PIPE_WRITE)
    {
      ports_done_with_port (user);
      return EBADF;
    }

  if (!pe->data)
    {
      ports_done_with_port (user);
      return EPIPE;
    }
  
  p = pe->data;

  if (p->data + p->chars_alloced - p->end < datalen)
    {
      struct pipe *newpipe;
      int oldamt;
      
      oldamt = p->end - p->start;

      if (p->start != p->data)
	{
	  /* There is free space at the front of the buffer.
	     Make a brand new pipe structure and copy all the
	     relevant state. */
	  newpipe = malloc (sizeof (struct pipe) + oldamt + datalen);
	  bcopy (p->start, newpipe->data, oldamt);
	  
	  newpipe->start = newpipe->data;
	  newpipe->end = newpipe->start + oldamt;
	  newpipe->chars_alloced = oldamt + datalen;
	  
	  newpipe->rq_head = p->rq_head;
	  newpipe->rq_tail = p->rq_tail;

	  newpipe->st = p->st;
	  
	  pe->data = newpipe;
	  pe->peer->data = newpipe;
	  
	  free (p);
	  p = newpipe;
	}
      else
	{
	  /* There is no free space at the front, so a 
	     simple realloc will do. */
	  newpipe = realloc (p, sizeof (struct pipe) + oldamt + datalen);
	  if (newpipe != p)
	    {
	      /* If it got moved, update the pointers to it. */
	      p = newpipe;
	      pe->data = newpipe;
	      pe->peer->data = newpipe;

	      /* Also update our pointers into the data array. */
	      p->start = p->data;
	      p->end = p->data + oldamt;
	    }

	  p->chars_alloced += datalen;
	}
    }

  assert (p->data + p->chars_alloced - p->end >= datalen);

  bcopy (data, p->end, datalen);
  p->end += datalen;
  *amount = datalen;
  update_time (&p->st->st_mtime, &p->st->st_mtime_usec);

  while (p->rq_head && (p->end - p->start))
    {
      if (p->rq_head->amount > p->end - p->start)
	p->rq_head->amount = p->end - p->start;
      io_read_reply (p->rq_head->reply, p->rq_head->replytype, 0,
		     p->start, p->rq_head->amount);
      p->start += p->rq_head->amount;
      
      p->rq_head = p->rq_head->next;
      if (!p->rq_head)
	p->rq_tail = 0;
    }

  notify_selects (&p->pending_selects);

  ports_done_with_port (user);
  return 0;
}

kern_return_t
S_io_read (mach_port_t reqport,
	   mach_port_t reply,
	   mach_msg_type_name_t replytype,
	   char **data,
	   mach_msg_type_number_t *datalen,
	   off_t offset,
	   mach_msg_type_number_t amount)
{
  struct pipe *p;
  struct pipe_end *pe;
  struct pipe_user *user = ports_check_port_type (reqport, PT_SOCKET);
  struct read_queue *rq;

  if (!user)
    return EOPNOTSUPP;
  
  pe = user->pe;

  if (pe->type != PIPE_READ)
    {
      ports_done_with_port (user);
      return EBADF;
    }
  
  p = pe->data;
  
  if (p->end - p->start)
    {
      if (amount > p->end - p->start)
	amount = p->end - p->start;

      if (*datalen < amount)
	vm_allocate (mach_task_self (), (vm_address_t *)data, amount, 1);
      *datalen = amount;
      bcopy (p->start, *data, amount);
      p->start += amount;
      update_time (&p->st->st_atime, &p->st->st_atime_usec);
      ports_done_with_port (user);
      return 0;
    }
  
  if (!pe->peer)
    {
      /* This is EOF */
      *datalen = 0;
      ports_done_with_port (user);
      return 0;
    }
  
  rq = malloc (sizeof (struct read_queue));
  rq->reply = reply;
  rq->replytype = replytype;
  rq->amount = amount;
  rq->reqport = reqport;
  
  rq->next = 0;
  if (p->rq_tail)
    p->rq_tail->next = rq;
  else
    p->rq_head = rq;
  p->rq_tail = rq;
  
  ports_done_with_port (user);
  return MIG_NO_REPLY;
}

kern_return_t
S_interrupt_operation (mach_port_t reqport)
{
  struct pipe_user *user = ports_check_port_type (reqport, PT_SOCKET);
  struct read_queue *rq, *nxt, *prv;
  
  /* The only thing we delay is reads */
  if (!user)
    return 0;
  else if (user->pe->type != PIPE_READ)
    {
      ports_done_with_port (user);
      return 0;
    }
  
  /* Scan through the pending reads list and abort reads on this
     port. */
  for (rq = user->pe->data->rq_head, prv = 0; rq; rq = nxt)
    {
      if (rq->reqport == reqport)
	{
	  io_read_reply (rq->reply, rq->replytype, EINTR, 0, 0);
	  if (prv)
	    prv->next = rq->next;
	  else
	    user->pe->data->rq_head = rq->next;
	  nxt = rq->next;
	  free (rq);
	}
      else
	{
	  prv = rq;
	  nxt = rq->next;
	}
    }
  ports_done_with_port (user);
  return 0;
}

kern_return_t
S_io_readable (mach_port_t reqport,
	       mach_port_t reply,
	       mach_msg_type_name_t replytype,
	       mach_msg_type_number_t *amount)
{
  struct pipe_user *user = ports_check_port_type (reqport, PT_SOCKET);

  if (!user)
    return EOPNOTSUPP;
  if (user->pe->type != PIPE_READ)
    {
      ports_done_with_port (user);
      return EBADF;
    }
  
  *amount = user->pe->data->end - user->pe->data->start;
  ports_done_with_port (user);
  return 0;
}

kern_return_t
S_io_seek (mach_port_t reqport,
	   mach_port_t reply,
	   mach_msg_type_name_t replytype,
	   off_t offset,
	   int whence,
	   off_t *newp)
{
  struct pipe_user *user = ports_check_port_type (reqport, PT_SOCKET);
  error_t err;

  if (user)
    {
      err = ESPIPE;
      ports_done_with_port (user);
    }
  else 
    err = EOPNOTSUPP;
  return err;
}


kern_return_t
S_io_select (io_t reqport,
	     mach_port_t reply,
	     mach_msg_type_name_t replyPoly,
	     int select_type,
	     mach_port_t return_port,
	     mach_msg_type_name_t return_portPoly,
	     int id_tag,
	     int *select_result)
{
  struct pipe_user *user = ports_check_port_type (reqport, PT_SOCKET);

  if (!user || select_type != SELECT_READ)
    return EOPNOTSUPP;

  if (user->pe->type != PIPE_READ)
    {
      ports_done_with_port (user);
      return EBADF;
    }

  if (user->pe->data->end > user->pe->data->start)
    {
      *select_result = SELECT_READ;
      mach_port_deallocate (mach_task_self (), return_port);
    }
  else
    {
      /* Queue a notification request.  */
      struct select_req *req = malloc (sizeof *req);
      req->reply = return_port;
      req->replytype = return_portPoly;
      req->idtag = id_tag;
      req->next = user->pe->data->pending_selects;
      user->pe->data->pending_selects = req;
      *select_result = 0;
    }

  ports_done_with_port (user);
  return 0;
}


kern_return_t
S_io_stat (mach_port_t reqport,
	   mach_port_t reply,
	   mach_msg_type_name_t replytype,
	   struct stat *st)
{
  struct pipe_user *user = ports_check_port_type (reqport, PT_SOCKET);
  error_t err;

  if (!user)
    return EOPNOTSUPP;
  
  if (!user->pe->data)
    err = user->pe->type == PIPE_WRITE ? EPIPE : ENOTCONN;
  else
    {
      bcopy (user->pe->data->st, st, sizeof (struct stat));
      err = 0;
    }
  ports_done_with_port (user);
  return err;
}

kern_return_t
S_io_get_openmodes (mach_port_t reqport,
		    mach_port_t reply,
		    mach_msg_type_name_t replytype,
		    int *bits)
{
  struct pipe_user *user = ports_check_port_type (reqport, PT_SOCKET);
  
  if (!user)
    return EOPNOTSUPP;

  switch (user->pe->type)
    {
    case PIPE_NONE:
      *bits = (O_READ | O_WRITE);
      break;
      
    case PIPE_READ:
      *bits = O_READ;
      break;
      
    case PIPE_WRITE:
      *bits = O_WRITE;
      break;
    }
  ports_done_with_port (user);
  return 0;
}

kern_return_t
S_io_reauthenticate (mach_port_t reqport,
		     mach_port_t reply,
		     mach_msg_type_name_t replytype,
		     mach_port_t rend_port)
{
  struct pipe_user *newuser, *user;
  uid_t gen_uids[10], gen_gids[10], aux_uids[10], aux_gids[10];
  uid_t *gubuf, *ggbuf, *aubuf, *agbuf;
  u_int ngu, ngg, nau, nag;
  mach_port_t auth_server;
  error_t err;

  user = ports_check_port_type (reqport, PT_SOCKET);
  if (!user)
    return EOPNOTSUPP;

  newuser = ports_allocate_port (sizeof (struct pipe_user), PT_SOCKET);
  ngu = ngg = nau = nag = 10;
  gubuf = gen_uids; ggbuf = gen_gids;
  aubuf = aux_uids; agbuf = aux_gids;
  auth_server = getauth ();
  err = auth_server_authenticate (auth_server, ports_get_right (user), 
				  MACH_MSG_TYPE_MAKE_SEND, 
				  rend_port, MACH_MSG_TYPE_MOVE_SEND,
				  ports_get_right (newuser), 
				  MACH_MSG_TYPE_MAKE_SEND, 
				  &gubuf, &ngu, 
				  &aubuf, &nau,
				  &ggbuf, &ngg,
				  &agbuf, &nag);
  assert (!err);		/* XXX */
  mach_port_deallocate (mach_task_self (), auth_server);

  /* Throw away the ID we went through all that trouble to get... */
  if (gen_uids != gubuf)
    vm_deallocate (mach_task_self (), (vm_address_t) gubuf, 
		   ngu * sizeof (uid_t));
  if (aux_uids != aubuf)
    vm_deallocate (mach_task_self (), (vm_address_t) aubuf,
		   nau * sizeof (uid_t));
  if (gen_gids != ggbuf)
    vm_deallocate (mach_task_self (), (vm_address_t) ggbuf,
		   ngg * sizeof (uid_t));
  if (aux_gids != agbuf)
    vm_deallocate (mach_task_self (), (vm_address_t) agbuf,
		   nag * sizeof (uid_t));
  
  newuser->pe = user->pe;
  newuser->pe->refcount++;
  ports_done_with_port (user);
  return 0;
}

inline kern_return_t
S_io_duplicate (mach_port_t reqport,
		mach_port_t reply,
		mach_msg_type_name_t replytype,
		mach_port_t *newobject,
		mach_msg_type_name_t *newobjecttype)
{
  struct pipe_user *newuser, *user;
  
  user = ports_check_port_type (reqport, PT_SOCKET);
  if (!user)
    return EOPNOTSUPP;
  
  newuser = ports_allocate_port (sizeof (struct pipe_user), PT_SOCKET);
  newuser->pe = user->pe;
  newuser->pe->refcount++;
  *newobject = ports_get_right (newuser);
  *newobjecttype = MACH_MSG_TYPE_MAKE_SEND;
  ports_done_with_port (user);
  return 0;
}


kern_return_t
S_io_restrict_auth (mach_port_t reqport,
		    mach_port_t reply,
		    mach_msg_type_name_t replytype,
		    mach_port_t *newobject,
		    mach_msg_type_name_t *newobjecttype,
		    uid_t *uids,
		    u_int nuids,
		    uid_t *gids,
		    u_int ngids)
{
  return S_io_duplicate (reqport, reply, replytype, newobject, newobjecttype);
}


/* Socket interface definitions */
kern_return_t
S_socket_create (mach_port_t reqport,
		 int sock_type,
		 int protocol,
		 mach_port_t *newu,
		 mach_msg_type_name_t *newutype)
{
  struct pipe_user *user;
  struct port_info *pi;

  pi = ports_check_port_type (reqport, PT_TRIVFS_PROTID);
  if (!pi)
    return EOPNOTSUPP;
  ports_done_with_port (pi);
  
  if (sock_type != SOCK_STREAM)
    return ESOCKTNOSUPPORT;
  if (protocol != 0)
    return ENOPROTOOPT;
  
  user = ports_allocate_port (sizeof (struct pipe_user), PT_SOCKET);
  user->pe = malloc (sizeof (struct pipe_end));
  user->pe->refcount = 1;
  user->pe->type = PIPE_NONE;
  user->pe->peer = 0;
  user->pe->data = 0;

  *newu = ports_get_right (user);
  *newutype = MACH_MSG_TYPE_MAKE_SEND;

  return 0;
}

kern_return_t
S_socket_connect2 (mach_port_t userport1,
		   mach_port_t userport2)
{
  struct pipe_user *user1, *user2;
  struct pipe_end *end1, *end2;
  struct pipe *p;
  
  user1 = ports_check_port_type (userport1, PT_SOCKET);
  if (!user1)
    return EOPNOTSUPP;
  user2 = ports_check_port_type (userport2, PT_SOCKET);
  if (!user2)
    {
      ports_done_with_port (user1);
      return EOPNOTSUPP;
    }
  
  end1 = user1->pe;
  end2 = user2->pe;

  if (end1->peer || end2->peer)
    {
      ports_done_with_port (user1);
      ports_done_with_port (user2);
      return EISCONN;
    }
    
  end1->peer = end2;
  end2->peer = end1;
  p = malloc (sizeof (struct pipe) + INIT_PIPE_BUF);
  p->start = p->data;
  p->end = p->data;
  p->chars_alloced = INIT_PIPE_BUF;
  p->st = malloc (sizeof (struct stat));
  bzero (p->st, sizeof (struct stat));
  update_time (&p->st->st_mtime, &p->st->st_mtime_usec);
  update_time (&p->st->st_atime, &p->st->st_atime_usec);
  update_time (&p->st->st_ctime, &p->st->st_ctime_usec);
  p->st->st_blksize = INIT_PIPE_BUF;
  p->st->st_fstype = FSTYPE_SOCKET;
  p->st->st_fsid = PF_LOCAL;
  p->st->st_ino = (ino_t) p;
  p->rq_head = p->rq_tail = 0;

  end1->data = p;
  end2->data = p;

  ports_done_with_port (user1);
  ports_done_with_port (user2);
  mach_port_deallocate (mach_task_self (), userport2);
  return 0;
}

kern_return_t
S_socket_shutdown (mach_port_t reqport,
		   int direction)
{
  struct pipe_user *user = ports_check_port_type (reqport, PT_SOCKET);
  struct pipe_end *pe;
  
  if (!user)
    return EOPNOTSUPP;
  pe = user->pe;
  
  if (!pe->peer)
    {
      ports_done_with_port (user);
      return ENOTCONN;
    }

  if (direction == 0)
    {
      /* This is shutting down reads, so it's a write socket. */
      if (pe->type == PIPE_READ)
	return EINVAL;
      pe->type = PIPE_WRITE;
      pe->peer->type = PIPE_READ;
      ports_done_with_port (user);
      return 0;
    }
  else if (direction == 1)
    {
      /* This is shutting down writes, so its a read socket. */
      if (pe->type == PIPE_WRITE)
	return EINVAL;
      pe->type = PIPE_READ;
      pe->peer->type = PIPE_WRITE;
      ports_done_with_port (user);
      return 0;
    }
  else
    {
      ports_done_with_port (user);
      return EINVAL;
    }
}

void
pipe_user_clean (void *arg)
{
  struct pipe_user *user = arg;
  struct pipe_end *pe = user->pe;

  if (--pe->refcount)
    return;

  /* There are five cases to consider.  */

  /* Case 1: close of read half on running pipe. */
  if (pe->type == PIPE_READ && pe->peer)
    {
      struct read_queue *rq, *tmp;
      /* Drop pending reads. */
      for (rq = pe->data->rq_head; rq; rq = tmp)
	{
	  mach_port_deallocate (mach_task_self (), rq->reply);
	  tmp = rq->next;
	  free (rq);
	}
      /* Free pipe itself. */
      free (pe->data->st);
      free (pe->data);
      /* Make peer notice we are gone in io_write. */
      pe->peer->peer = 0;
      free (pe);
    }
  
  /* Case 2: close of write half on running pipe. */
  else if (pe->type == PIPE_WRITE && pe->peer)
    {
      struct read_queue *rq, *tmp;
      /* Wakeup pending reads with EOF */
      for (rq = pe->data->rq_head; rq; rq = tmp)
	{
	  io_read_reply (rq->reply, rq->replytype, 0, 0, 0);
	  tmp = rq->next;
	  free (rq);
	}
      pe->data->rq_head = pe->data->rq_tail = 0;

      notify_selects (&pe->data->pending_selects);

      /* Make peer notifice we are gone in io_read */
      pe->peer->peer = 0;
      free (pe);
    }
  
  /* Case 3: close of read half when peer has already closed above. */
  else if (pe->type == PIPE_READ && !pe->peer)
    {
      assert (!pe->data->rq_head);
      free (pe->data->st);
      free (pe->data);
      free (pe);
    }
  
  /* Case 4: close of write half when peer has already closed above. */
  else if (pe->type == PIPE_WRITE && !pe->peer)
    {
      free (pe);
    }
  
  /* Case 5: clase of socket prior to twin socket_shutdown calls; return
     peer to unconnected state if we have one. */
  else
    {
      if (pe->peer)
	{
	  pe->peer->peer = 0;
	  pe->peer->data = 0;
	}
      if (pe->data)
	{
	  free (pe->data->st);
	  free (pe->data);
	}
      free (pe);
    }
}

kern_return_t
do_mach_notify_no_senders (mach_port_t nosendersport,
			   mach_port_mscount_t mscount)
{
  void *pi = ports_get_port (nosendersport);
  if (pi)
    {
      ports_no_senders (pi, mscount);
      ports_done_with_port (pi);
      return 0;
    }
  else
    return EOPNOTSUPP;
}

void
update_time (time_t *secs, u_long *usecs)
{
  time_value_t current_time;
  
  host_get_time (mach_host_self (), &current_time);
  *secs = current_time.seconds;
  *usecs = current_time.microseconds;
}

/* Unused client stubs */
kern_return_t
S_socket_listen (mach_port_t req,
		 int limit)
{
  return EOPNOTSUPP;
}

kern_return_t
S_socket_accept (mach_port_t req,
		 mach_port_t *con,
		 mach_msg_type_name_t *contype,
		 mach_port_t *addr,
		 mach_msg_type_name_t *addrtype)
{
  return EOPNOTSUPP;
}

kern_return_t
S_socket_connect (mach_port_t req,
		  mach_port_t addr)
{
  return EOPNOTSUPP;
}

kern_return_t
S_socket_bind (mach_port_t req,
	       mach_port_t addr)
{
  return EOPNOTSUPP;
}

kern_return_t
S_socket_name (mach_port_t req,
	       mach_port_t *addr,
	       mach_msg_type_name_t *addrtype)
{
  return EOPNOTSUPP;
}

kern_return_t
S_socket_peername (mach_port_t req,
		   mach_port_t *addr,
		   mach_msg_type_name_t *addrtype)
{
  return EOPNOTSUPP;
}

kern_return_t
S_socket_create_address (mach_port_t req,
			 int type,
			 char *data,
			 u_int datalen,
			 mach_port_t *addr,
			 mach_msg_type_name_t *addrtype,
			 int binding)
{
  return EOPNOTSUPP;
}

kern_return_t
S_socket_fabricate_address (mach_port_t req,
			    int sockaddr_type,
			    mach_port_t *addr,
			    mach_msg_type_name_t *addrtype)
{
  return EOPNOTSUPP;
}

kern_return_t
S_socket_whatis_address (mach_port_t addr,
			 int *type,
			 char **data,
			 u_int *datalen)
{
  return EOPNOTSUPP;
}

kern_return_t
S_socket_getopt (mach_port_t req,
		 int level,
		 int opt,
		 char **value,
		 u_int *valuelen)
{
  return EOPNOTSUPP;
}

kern_return_t
S_socket_setopt (mach_port_t req,
		 int level,
		 int opt,
		 char *value,
		 u_int valuelen)
{
  return EOPNOTSUPP;
}

kern_return_t
S_socket_send (mach_port_t req,
	       mach_port_t addr,
	       int flags,
	       char *data,
	       u_int datalen,
	       mach_port_t *ports,
	       u_int nports,
	       char *control,
	       u_int controllen,
	       int *amount)
{
  return EOPNOTSUPP;
}

kern_return_t
S_socket_recv (mach_port_t sock,
	       mach_port_t *addr,
	       int flags,
	       char **data,
	       u_int *datalen,
	       mach_port_t **ports,
	       mach_msg_type_name_t *portstype,
	       u_int *nports,
	       char **cntl,
	       u_int *cntllen,
	       int *outflags,
	       int amount)
{
  return EOPNOTSUPP;
}


kern_return_t
do_mach_notify_port_deleted (mach_port_t notify,
			     mach_port_t name)
{
  return 0;
}

kern_return_t
do_mach_notify_msg_accepted (mach_port_t notify,
			     mach_port_t name)
{
  return 0;
}

kern_return_t
do_mach_notify_port_destroyed (mach_port_t notify,
			       mach_port_t name)
{
  return 0;
}

kern_return_t
do_mach_notify_send_once (mach_port_t notify)
{
  return 0;
}

kern_return_t
do_mach_notify_dead_name (mach_port_t notify,
			  mach_port_t deadport)
{
  return 0;
}

kern_return_t 
S_io_set_all_openmodes (io_t io_object,
			mach_port_t reply,
			mach_msg_type_name_t replyPoly,
			int newbits)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_set_some_openmodes(io_t io_object,
			mach_port_t reply,
			mach_msg_type_name_t replyPoly,
			int bits_to_set)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_clear_some_openmodes(io_t io_object,
			  mach_port_t reply,
			  mach_msg_type_name_t replyPoly,
			  int bits_to_clear)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_async(io_t io_object,
	   mach_port_t reply,
	   mach_msg_type_name_t replyPoly,
	   mach_port_t notify_port,
	   mach_port_t *async_id_port,
	   mach_msg_type_name_t *async_id_portPoly)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_mod_owner(io_t io_object,
	       mach_port_t reply,
	       mach_msg_type_name_t replyPoly,
	       pid_t owner)
{
  return EOPNOTSUPP;
}

kern_return_t 
S_io_get_owner(io_t io_object,
	       mach_port_t reply,
	       mach_msg_type_name_t replyPoly,
	       pid_t *owner)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_get_icky_async_id (io_t io_object,
			mach_port_t reply,
			mach_msg_type_name_t replyPoly,
			mach_port_t *icky_async_id_port,
			mach_msg_type_name_t *icky_async_id_portPoly)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_map (io_t io_object,
	  mach_port_t reply,
	  mach_msg_type_name_t replyPoly,
	  mach_port_t *memobjrd,
	  mach_msg_type_name_t *memobjrdPoly,
	  mach_port_t *memobjwt,
	  mach_msg_type_name_t *memobjwtPoly)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_map_cntl (mach_port_t obj,
	       mach_port_t reply_port,
	       mach_msg_type_name_t reply_type,
	       mach_port_t *mem,
	       mach_msg_type_name_t *memtype)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_get_conch (mach_port_t obj,
		mach_port_t reply_port,
		mach_msg_type_name_t reply_type)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_release_conch (mach_port_t obj,
		    mach_port_t reply_port,
		    mach_msg_type_name_t reply_type)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_eofnotify (mach_port_t obj,
		mach_port_t reply_port,
		mach_msg_type_name_t reply_type)

{
  return EOPNOTSUPP;
}

kern_return_t
S_io_prenotify (mach_port_t obj,
		mach_port_t reply_port,
		mach_msg_type_name_t reply_type,
		vm_offset_t start,
		vm_offset_t end)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_postnotify (mach_port_t obj,
		 mach_port_t reply_port,
		 mach_msg_type_name_t reply_type,
		 vm_offset_t start,
		 vm_offset_t end)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_readsleep (mach_port_t obj,
		mach_port_t reply_port,
		mach_msg_type_name_t reply_type)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_readnotify (mach_port_t obj,
		 mach_port_t reply_port,
		 mach_msg_type_name_t reply_type)
{
  return EOPNOTSUPP;
}


kern_return_t
S_io_sigio (mach_port_t obj,
	    mach_port_t reply_port,
	    mach_msg_type_name_t reply_type)
{
  return EOPNOTSUPP;
}

kern_return_t
S_io_server_version (mach_port_t obj,
		     mach_port_t reply,
		     mach_msg_type_name_t reply_type,
		     char *name,
		     int *maj,
		     int *min,
		     int *edit)
{
  return EOPNOTSUPP;
}
