/* Startup server
   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.  */

mach_port_t host_priv, master_device;

/* This structure keeps track of each registered essential task. */
struct ess_task
{
  task_t task_port;
  char *name;
  struct ess_task *next;
};

/* This structure keeps track of each notified task.  */
struct ntfy_task
{
  mach_port_t notify_port;
  struct ntfy_task *next;
};

/* These are linked lists of all of the registered items. */
struct ess_task *ess_tasks;
struct ntfy_task *ntfy_tasks;

startup_t norm_port, priv_port;
mach_port_t notify_port;

error_t
startup_essential_task (startup_t port,
			task_t task,
			char *name,
			u_int namelen)
{
  struct ess_task *et;
  mach_port_t prev;

  if (port != priv_port)
    return POSIX_EPERM;
  
  mach_port_request_notification (mach_task_self (), task, 
				  MACH_NOTIFY_DEAD_NAME, 1, notify_port, 
				  MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
  if (prev)
    mach_port_deallocate (mach_task_self (), prev);
  task_set_special_port (task, TASK_EXCEPTION_PORT, notify_port);
  et = malloc (sizeof (struct ess_task));
  et->task_port = task;
  et->name = malloc (namelen);
  bcopy (name, et->name, namelen);
  et->next = ess_tasks;
  ess_tasks = et;
  return POSIX_SUCCESS;
}

error_t
startup_request_notification (startup_t port,
			      mach_port_t their_port)
{
  struct ntfy_task *nt;
  mach_port_t prev;
  
  mach_port_request_notification (mach_task_self (), their_port, 
				  MACH_NOTIFY_DEAD_NAME, 1, notify_port, 
				  MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
  if (prev)
    mach_port_deallocate (mach_task_self (), prev);
  nt = malloc (sizeof (struct ntfy_task));
  nt->their_port = port;
  nt->next = ntfy_tasks;
  ntfy_tasks = nt;
  return POSIX_SUCCESS;
}

/* Called whenever we get a dead name notification. */
dead_name_notification (mach_port_t name)
{
  struct ntfy_task *nt, *pnt;
  struct ess_task *et;
  
  for (nt = ntfy_tasks, pnt = 0; nt; pnt = nt, nt = nt->next)
    {
      if (nt->notify_port == name)
	{
	  mach_port_mod_refs (mach_task_self (), MACH_PORT_RIGHT_SEND, -2);
	  if (pnt)
	    pnt->next = nt->next;
	  else
	    ntfy_tasks = nt->next;
	  free (nt);
	  return;
	}
    }
  for (et = ntfy_tasks; et; et = et->next)
    if (et->task_port == name)
      {
	et->task_port = 0;	/* avoid core dump */
	crash_system (et, 0);
      }
}

error_t
catch_exception_raise (mach_port_t exc_port,
		       thread_t thread,
		       task_t task,
		       int exc,
		       int code,
		       int subcode)
{
  struct ess_task *et;
  
  for (et = ess_tasks; et; et = et->next)
    if (et->task_port == task)
      crash_system (et, thread);
  return 0;
}

error_t
main ()
{
  mach_port_t bootport;
  file_t root;
  
  /* The filesystem has gotten us running somehow; we need to get from
     it some crucial information.  */
  
  task_get_special_port (mach_task_self (), TASK_BOOTSTRAP_PORT, &bootport);
  
  startup_awaywego (bootport, &host_priv, &device_master, &root);
  
  setcrdir (root);
  
  mach_port_deallocate (mach_task_self (), root);
  
  
