/* Name registry service
   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 "name.h"
#include "startup.h"
#include <mach/error.h>
#include <gnu/errno.h>
#include <gnu/posix_errors.h>
#include <mach/mig_errors.h>
#include <mach/notify.h>

#define HASH_SIZE 31

struct bucket 
{
  mach_port_t port;
  struct bucket *next;
  char name[0];
};

struct bucket *htable[HASH_SIZE];
void name_deadname (mach_port_t);
mach_port_t name_port;


int
request_server (mach_msg_header_t *inp,
		mach_msg_header_t *outp)
{
  mach_dead_name_notification_t *inp_dn;
  mig_reply_header_t *repl;
  extern int name_server ();

  if (inp->msgh_kind == MACH_MSGH_KIND_NOTIFICATION)
    {
      inp_dn = (mach_dead_name_notification_t *)inp;
      repl = (mig_reply_header_t *)outp;
      if (inp_dn->not_type.msgt_name != MACH_MSG_TYPE_PORT_NAME
	  || (MACH_MSGH_BITS_REMOTE (inp->msgh_bits)
	      != MACH_MSG_TYPE_MOVE_SEND_ONCE)
	  || inp->msgh_id != MACH_NOTIFY_DEAD_NAME)
	{
	  printf ("Bad request notification\n");
	  return 0;
	}
      name_deadname (inp_dn->not_port);
      repl->RetCode = MIG_NO_REPLY;
      return 1;
    }
  return name_server (inp, outp);
}

void
main ()
{
  mach_port_t bootport;
  extern void mach_msg_server ();

  mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &name_port);
  task_get_special_port (mach_task_self (), TASK_BOOTSTRAP_PORT, &bootport);
  startup_nameinit (bootport, name_port);
  
  while (1)
    mach_msg_server (request_server, vm_page_size * 2, name_port);
}


/* Server functions */
void
name_deadname (mach_port_t name)
{
  int slot;
  struct bucket *b, *prev, *tmp;
  
  /* This is rare, so it's OK if it's slow */
  for (slot = 0; slot < HASH_SIZE; slot++)
    for (b = htable[slot], prev = 0; b;)
      if (b->port == name)
	{
	  if (prev)
	    prev->next = b->next;
	  else
	    htable[slot] = b->next;
	  
	  tmp = b;
	  b = b->next;
	  free (tmp);
	}
      else
	{
	  prev = b;
	  b = b->next;
	}
  
  mach_port_deallocate (mach_task_self (), name);
}

int
dohash (char *name,
	u_int namelen)
{
  int sum = 0;
  while (namelen--)
    sum += *name++;
  return sum;
}

error_t
name_getport (mach_port_t nm,
	      char *name,
	      u_int namelen,
	      mach_port_t *pt)
{
  struct bucket *b;
  
  for (b = htable[dohash (name, namelen)]; b; b = b->next)
    if (!strncmp (b->name, name, namelen))
      {
	*pt = b->port;
	return POSIX_SUCCESS;
      }
  return POSIX_ESRCH;
}

error_t
name_registerport (mach_port_t nm,
		   char *name,
		   u_int namelen,
		   mach_port_t pt)
{
  struct bucket *b;
  int hashcode = dohash (name, namelen);
  mach_port_t unused;
  
  for (b = htable[hashcode]; b; b = b->next)
    if (!strncmp (b->name, name, namelen))
      return POSIX_EEXIST;

  b = malloc (sizeof (struct bucket) + namelen);
  bcopy (name, b->name, namelen);
  b->port = pt;
  b->next = htable[hashcode];
  htable[hashcode] = b;
  mach_port_request_notification (mach_task_self (), pt, MACH_NOTIFY_DEAD_NAME,
				  1, name_port, MACH_MSG_TYPE_MAKE_SEND_ONCE,
				  &unused);
  return POSIX_SUCCESS;
}

	
  
 
