/* Implementation of the process interface
   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.  */

#include "proc.h"
#include "process.h"
#include "misc.h"
#include "auth.h"
#include "startup.h"

long hostid;
char hostname[MAXHOSTNAMELEN];

/* System version info XXX */
#define VERS_SYSNAME "GNU Hurd"
#define VERS_RELEASE "-1"
#define VERS_VERSION "0"
#define VERS_MACHINE "i386"

error_t
proc_sethostid (struct proc *p,
		int newhostid)
{
  if (p->p_idblock.euid)
    return POSIX_EPERM;

  hostid = newhostid;
  return 0;
}

error_t
proc_gethostid (struct proc *p,
		int *outhostid)
{
  *outhostid = hostid;
  return 0;
}

error_t
proc_sethostname (struct proc *p,
		  char *newhostname)
{
  if (p->p_idblock.euid)
    return POSIX_EPERM;
  
  if (strlen (newhostname) > MAXHOSTNAMELEN - 1)
    return POSIX_EINVAL;
  
  strcpy (hostname, newhostname);
  return 0;
}

error_t
proc_gethostname (struct proc *p,
		  char *outhostname)
{
  strcpy (outhostname, hostname);
  return 0;
}

error_t
proc_getpids (struct proc *p,
	      pid_t *ourpid,
	      pid_t *ourppid,
	      int *orphaned)
{
  *ourpid = p->p_pid;
  *ourppid = p->p_parent->p_pid;
  *orphaned = ! p->p_pgrp->pg_orphcnt;
  return 0;
}

error_t
proc_reauthenticate (struct proc *p)
{
  auth_server_authenticate (authserver, p->p_reqport,
			    MACH_PORT_NULL, &p->p_idblock);
  return 0;
}

error_t
proc_register (struct proc *p,
	       task_t t,
	       sigthread_t sigport,
	       mach_port_t *childpp)
{
  struct proc *newp;
  processor_set_name_t psetname;
  processor_set_t pset;
  task_array_t tasks;
  u_int ntasks;
  int i;
  error_t error;
  int found = 0;
  mach_port_t unused;

  /* Check if this task is known to our host.  If the task returns a
     bogus value for its processor set, we will catch that with
     host_processor_set_priv.  If the task returns a processor set which
     doesn't include it, that will be caught with processor_set_tasks and
     the following loop.  */
  task_get_assignment (t, &psetname);
  error = host_processor_set_priv (host_priv, psetname, &pset);
  mach_port_deallocate (mach_task_self (), psetname);
  if (error)
    {
      mach_port_deallocate (mach_task_self (), t);
      mach_port_deallocate (mach_task_self (), sigport);
      return POSIX_EPERM;
    }

  processor_set_tasks (pset, &tasks, &ntasks);
  mach_port_deallocate (mach_task_self (), pset);
  for (i = 0; i < ntasks; i++)
    {
      if (tasks[i] == t)
	found = 1;
      mach_port_deallocate (mach_task_self (), tasks[i]);
    }
  vm_deallocate (mach_task_self (), (vm_address_t) tasks,
		 ntasks * sizeof (task_t));

  if (!found)
    {
      mach_port_deallocate (mach_task_self (), t);
      mach_port_deallocate (mach_task_self (), sigport);
      return POSIX_EPERM;
    }

  /* Now we have a trustable task port, and we can proceed.  */
  newp = make_process_struct ();
  
  newp->p_task = t;
  newp->p_pid = genpid ();
  newp->p_login = p->p_login;
  p->p_login->l_refcnt++;

  mach_port_request_notification (mach_task_self (), t, MACH_NOTIFY_DEAD_NAME,
				  1, newp->p_reqport, 
				  MACH_MSG_TYPE_MAKE_SEND_ONCE, &unused);

  newp->p_argv = p->p_argv;
  newp->p_envp = p->p_envp;

  newp->p_parent = p;
  newp->p_ochild = 0;
  newp->p_sib = p->p_ochild;
  p->p_ochild = newp->p_sib;
  newp->p_pgrp = p->p_pgrp;
  newp->p_pgid = p->p_pgid;
  newp->p_idblock = p->p_idblock;
  
  newp->p_sigthread = sigport;
  newp->p_nsignals = 0;
  newp->p_exit_status = 0;
  newp->p_stopsig = 0;

  newp->p_wc.pw_reply_port = MACH_PORT_NULL;

  newp->p_exec = 0;
  newp->p_stopped = 0;
  newp->p_system = 0;
  newp->p_waited = 0;
  newp->p_exited = 0;
  newp->p_waiting = 0;
  newp->p_traced = 0;
  newp->p_nostopcld = 0;
  
  paddhash (newp);
  join_pgrp (newp);
  
  *childpp = newp->p_reqport;
  return 0;
}

error_t
proc_setports (struct proc *callerp,
	       pid_t pid,
	       sigthread_t newport,
	       task_t newtask,
	       sigthread_t *oldport,
	       task_t *oldtask)
{
  struct proc *p = pfind (pid, 0);
  
  if (!p)
    return POSIX_ESRCH;
  
  if (p->p_idblock.euid != callerp->p_idblock.euid
      && callerp->p_idblock.euid)
    return POSIX_EPERM;

  *oldport = p->p_sigthread;
  if (newport)
    p->p_sigthread = newport;
  else
    mach_port_mod_refs (mach_task_self (), p->p_sigthread,
			MACH_PORT_RIGHT_SEND, 1);
  if (newtask)
    p->p_task = newtask;
  else
    mach_port_mod_refs (mach_task_self (), p->p_task, MACH_PORT_RIGHT_SEND, 1);
  return POSIX_SUCCESS;
}

static int
kill1 (struct proc *callerp,
       struct proc *p,
       mach_port_t ctty,
       int sig,
       int *stopped)
{
  if (ctty)
    {
      if (ctty != p->p_pgrp->pg_session->s_ctty)
	if (stopped)
	  *stopped = CATCH;	/* something other than IGNBLK */
	return 0;
    }
  else if (callerp->p_idblock.ruid != p->p_idblock.ruid 
	   && callerp->p_idblock.ruid != p->p_idblock.euid
	   && callerp->p_idblock.euid != p->p_idblock.ruid 
	   && callerp->p_idblock.euid != p->p_idblock.euid
	   && (sig != SIGCONT
	       || p->p_pgrp->pg_session != callerp->p_pgrp->pg_session))
    return 0;
  if (stopped)
    *stopped = psig (p, sig, !!ctty);
  else
    psig (p, sig, 0);
  return 1;
}

error_t
kill (struct proc *callerp,
      pid_t pid,
      int sig,
      mach_port_t ctty,
      pid_t stoppid,
      int *stopped)
{
  struct proc *p;
  struct proc *stopp = 0;
  struct pgrp *pg;
  int sentone;

  if (stoppid && !(stopp = pfind (stoppid, 0)))
    return POSIX_ESRCH;
  
  if (pid == -1)
    return POSIX_EINVAL;	/* XXX broadcast in BSD */
  
  if (sig < 0 || sig >= NSIG)
    return POSIX_EINVAL;
  
  *stopped = 0;
  
  if (pid <= 0)
    {
      if (!pid)
	pg = callerp->p_pgrp;
      else
	pg = pgfind (-pid);

      if (!pg)
	return POSIX_ESRCH;
      
      sentone = 0;
      if (stopp->p_pgrp == pg)
	{
	  sentone |= kill1 (callerp, stopp, ctty, sig, stopped);
	  if (*stopped == IGNBLK)
	    return POSIX_SUCCESS;
	}
      for (p = pg->pg_plist; p; p = p->p_next)
	if (p != stopp)
	  sentone |= kill1 (callerp, p, ctty, sig, 0);
    }
  else
    {
      p = pfind (pid, 0);
      if (!p)
	return POSIX_ESRCH;
      
      sentone = kill1 (callerp, p, ctty, sig, (p == stopp) ? 0 : stopped);
    }
  
  if (!sentone)
    return POSIX_EPERM;
  else
    return 0;
}

error_t
proc_kill (struct proc *p,
	   pid_t pid,
	   int signo)
{
  int unused;
  return kill (p, pid, signo, MACH_PORT_NULL, 1, &unused);
}

error_t
proc_ctty_kill (struct proc *callerp,
		pid_t pid,
		mach_port_t ctty,
		int signo,
		int stoppid,
		int *willstop)
{
  struct proc *p;
  
  mach_port_mod_refs (mach_task_self (), ctty, MACH_PORT_RIGHT_SEND, -1);

  p = pfind (stoppid, 0);
  if (stoppid == 0)
    return POSIX_ESRCH;

  switch (signo)
    {
      /* Don't send terminal sigs to orphaned processes.  */
    case SIGTSTP:
    case SIGTTIN:
    case SIGTTOU:
      if (!p->p_pgrp->pg_orphcnt)
	{
	  *willstop = ORPHANED;
	  return POSIX_SUCCESS;
	}
      /* fall through... */
    case SIGINT:
    case SIGQUIT:
    case SIGHUP:
      return kill (callerp, pid, signo, ctty, stoppid, willstop);
      
    default:
      return POSIX_EPERM;
    }
}

error_t
proc_set_ctty (struct proc *p,
	       mach_port_t ctty)
{
  if (p->p_pgrp->pg_session->s_leader != p)
    return POSIX_EPERM;
  mach_port_deallocate (mach_task_self (), p->p_pgrp->pg_session->s_ctty);
  p->p_pgrp->pg_session->s_ctty = ctty;
  return POSIX_SUCCESS;
}
	  
/* Check if we can return on a wait for process p right now.  If we
   can, return 1 and set wait_status and ru appropriately, otherwise
   return 0.  */
int
proc_check_wait(struct proc *p,
		int *wait_status,
		int options,
		struct rusage *ru,
		pid_t *childpid)
{
  /* ru XXX */
  if (p->p_exited)
    {
      *wait_status = p->p_exit_status;
      *childpid = p->p_pid;

      /* At this point the child process is gone for good, and we can
	 finish the deallocation.  */
      mach_port_destroy (mach_task_self (), p->p_reqport);
      mach_port_deallocate (mach_task_self (), p->p_sigthread);
      mach_port_deallocate (mach_task_self (), p->p_task);
      premhash (p);
      free (p);
      return 1;
    }
  
  if (p->p_stopped && !p->p_waited &&
      (p->p_traced || options & WUNTRACED) )
    {
      p->p_waited = 0;
      *wait_status = W_STOPCODE (p->p_stopsig);
      *childpid = p->p_pid;
      return 1;
    }

  return 0;
}

error_t
proc_wait (struct proc *parentp,
	   mach_port_t reply_port,
	   mach_msg_type_name_t reply_porttype,
	   pid_t pid,
	   int *wait_status,
	   int options,
	   struct rusage *ru,
	   pid_t *childpid)
{
  struct proc *p;
  
  bzero (ru, sizeof (struct rusage)); /* XXX */
  
  if (pid > 0)
    {
      /* Look for just this pid */
      p = pfind (pid, 1);
      if (!p || p->p_parent != parentp)
	return POSIX_ECHILD;
      
      if (proc_check_wait (p, wait_status, options, ru, childpid))
	return 0;
    }
  else if (pid == WAIT_ANY)
    {
      /* Examine all children */
      if (!parentp->p_ochild)
	return POSIX_ECHILD;
      
      for (p = parentp->p_ochild; p; p = p->p_sib)
	if (proc_check_wait (p, wait_status, options, ru, childpid))
	  return 0;
    }
  else
    {
      int found = 0;
      int pgid;
      
      /* Examine children in specified process group */
      if (pid == WAIT_MYPGRP)
	pgid = parentp->p_pgrp->pg_pgid;
      else
	pgid = -pid;

      for (p = parentp->p_ochild; p; p = p->p_sib)
	if (p->p_pgid == pgid)
	  {
	    found++;
	    if (proc_check_wait (p, wait_status, options, ru, childpid))
	      return 0;
	  }

      if (!found)
	return POSIX_ECHILD;
    }

  /* If we got here, then we have children we could wait on, but they
     don't have a status immediately available.  */

  if (options & WNOHANG)
    {
      *wait_status = 0;
      *childpid = 0;
      return 0;
    }

  /* Leave continuation information.  A reply will be sent later, when
     the child's state changes.  */

  parentp->p_wc.pw_reply_port = reply_port;
  parentp->p_wc.pw_reply_port_type = reply_porttype;
  parentp->p_wc.pw_pid = pid;
  parentp->p_wc.pw_options = options;
  parentp->p_waiting = 1;
  
  /* Don't send reply */
  return MIG_NO_REPLY;
}

error_t
proc_waitintr (struct proc *p)
{
  if (p->p_waiting)
    wait_continuation (p, 1);
  return POSIX_SUCCESS;
}

error_t
proc_dostop (struct proc *p,
	     thread_t contthread)
{
  thread_t *threads;
  int i;
  u_int nthreads;
  
  task_suspend (p->p_task);
  task_threads (p->p_task, &threads, &nthreads);
  for (i = 0; i < nthreads; i++)
    {
      if (threads[i] != contthread)
	thread_suspend (threads[i]);
      mach_port_deallocate (mach_task_self (), threads[i]);
    }
  mach_port_deallocate (mach_task_self (), contthread);
  task_resume (p->p_task);
  return 0;
}

error_t
proc_markstop (struct proc *p,
	       int signo)
{
  p->p_stopsig = signo;
  p->p_stopped = 1;
  p->p_waited = 0;
  if (!p->p_parent->p_nostopcld)
    psig (p->p_parent, SIGCHLD, 0);
  if (p->p_parent->p_waiting)
    wait_continuation (p->p_parent, 0);
  return 0;
}

error_t
proc_cont (struct proc *p)
{
  p->p_stopped = 0;
  return POSIX_SUCCESS;
}


error_t
proc_died (mach_port_t name)
{
  struct proc *p = (struct proc *)name;
  if (p->p_exited)
    return 0;
  
  /* Fake an exit by SIGKILL. */
  return proc_exit (p, WTERMSIG (SIGKILL));
}

error_t
proc_exit (struct proc *p,
	   int wait_status)
{
  struct proc *childp, *nextchild;
  struct proc *initproc = pfind (1, 0);
  
  if (!initproc)
    panic ("proc_exit: no init");

  leave_pgrp (p);

  /* We aren't the session leader any more! */
  if (p->p_pgrp->pg_session->s_leader == p)
    p->p_pgrp->pg_session->s_leader = 0;

  /* Reparent children */
  for (childp = p->p_ochild; childp; childp = nextchild)
    {
      nextchild = childp->p_sib;
      
      childp->p_parent = initproc;
      childp->p_sib = initproc->p_ochild;
      initproc->p_ochild = childp;
      proc_newids (childp->p_sigthread, 1, childp->p_pgrp->pg_pgid, 
		   !childp->p_pgrp->pg_orphcnt);
    }

  mach_port_deallocate (mach_task_self (), p->p_reqport);
  mach_port_deallocate (mach_task_self (), p->p_task);
  mach_port_deallocate (mach_task_self (), p->p_sigthread);
  if (p->p_waiting)
    mach_port_deallocate (mach_task_self (), p->p_wc.pw_reply_port);

  p->p_pgrp = 0;
  p->p_exited = 1;
  p->p_exit_status = wait_status;

  psig (p->p_parent, SIGCHLD, 0);

  if (p->p_parent->p_waiting)
    wait_continuation (p->p_parent, 0);

  /* Process will be removed from hash queue when parent picks up status. */

  return 0;
}

error_t
proc_mark_exec (struct proc *p)
{
  p->p_exec = 1;
  return 0;
}

error_t
proc_mark_traced (struct proc *p)
{
  p->p_traced = 1;
  return 0;
}

error_t
proc_mark_system (struct proc *p)
{
  if (p->p_idblock.euid)
    return POSIX_EPERM;
  else  
    {
      p->p_system = 1;
      return 0;
    }
}

error_t
proc_mark_nostopchild (struct proc *p,
		       int newval)
{
  p->p_nostopcld = !!newval;
  return POSIX_SUCCESS;
}

error_t
proc_setsid (struct proc *p)
{
  if (p->p_pgid == p->p_pid || pgfind (p->p_pid))
    return POSIX_EPERM;
  
  leave_pgrp (p);

  p->p_pgrp = malloc (sizeof (struct pgrp));
  p->p_pgrp->pg_plist = 0;
  p->p_pgrp->pg_pgid = p->p_pid;
  p->p_pgrp->pg_orphcnt = 0;
  p->p_pgrp->pg_session = malloc (sizeof (struct session));
  p->p_pgrp->pg_session->s_leader = p;
  p->p_pgrp->pg_session->s_count = 1;
  p->p_pgid = p->p_pid;

  pgaddhash (p->p_pgrp);

  join_pgrp (p);
  
  return 0;
}

error_t
proc_setpgrp (struct proc *callerp,
	      pid_t pid,
	      pid_t pgrp)
{
  struct proc *p;
  struct pgrp *pg;

  if (pgrp < 0)
    return POSIX_EINVAL;

  if (pid && pid != callerp->p_pid)
    {
      p = pfind (pid, 0);

      if (!p || p->p_parent != callerp
	  || p->p_pgrp->pg_session != callerp->p_pgrp->pg_session)
	return POSIX_EPERM;

      if (p->p_exec)
	return POSIX_EACCES;
    }
  else
    p = callerp;
  
  if (p->p_pgrp->pg_session->s_leader == p)
    return POSIX_EPERM;
  
  pg = pgfind (pgrp);
  
  if (pgrp != p->p_pid
      && (!pg
	  || pg->pg_session != p->p_pgrp->pg_session))
    return POSIX_EPERM;
  
  if (pgrp == p->p_pgid)
    return 0;
  
  if (!pg)
    {
      pg = malloc (sizeof (struct pgrp));
      pg->pg_plist = 0;
      pg->pg_pgid = pgrp;
      pg->pg_orphcnt = 0;
      pg->pg_session = p->p_pgrp->pg_session;
      pg->pg_session->s_count++;
      pgaddhash (pg);
    }
  
  leave_pgrp (p);
  p->p_pgrp = pg;
  p->p_pgid = pg->pg_pgid;
  join_pgrp (p);
  if (p != callerp)
    proc_newids (p->p_sigthread, p->p_parent->p_pid, pg->pg_pgid, 
		 !p->p_pgrp->pg_orphcnt);

  return 0;
}

error_t
proc_getpgrp (struct proc *callerp,
	      pid_t pid,
	      pid_t *pgrpp)
{
  struct proc *p = pfind (pid, 0);
  
  if (!p)
    return POSIX_ESRCH;
  
  *pgrpp = p->p_pgid;
  return 0;
}

error_t
proc_version (struct proc *p,
	      int which,
	      char *buf)
{
  char *s;
  
  /* We need to stick the microkernel version string in here somewhere.  XXX */
  switch (which)
    {
    case SYSNAME:
      s = VERS_SYSNAME;
      break;
    case RELEASE:
      s = VERS_RELEASE;
      break;
    case VERSION:
      s = VERS_VERSION;
      break;
    case MACHINE:
      s = VERS_MACHINE;
      break;
    default:
      return POSIX_EINVAL;
    }
  
  strcpy (buf, s);
  return 0;
}

error_t
proc_getrusage (struct proc *callerp,
		pid_t pid,
		int kind,
		struct rusage *ru)
{
  bzero (ru, sizeof (struct rusage)); /* XXX */
  return 0;
}

error_t
proc_setpriority (struct proc *callerp,
		  int which,
		  int who,
		  int prio)
{
  return POSIX_SUCCESS;		/* XXX */
}

error_t
proc_getpriority (struct proc *callerp,
		  int which,
		  int who,
		  int *prio)
{
  *prio = 0;			/* XXX */
  return POSIX_SUCCESS;
}

error_t
proc_pid2task (struct proc *callerp,
	       pid_t pid,
	       task_t *task)
{
  struct proc *p;

  switch (pid)
    {
    case -1:
      /* Host priv port. */
      if (callerp->p_idblock.euid)
	return POSIX_EPERM;
      *task = host_priv;
      break;

    case -2:
      /* Device master port. */
      if (callerp->p_idblock.euid)
	return POSIX_EPERM;
      *task = device_master;
      break;

    default:
      p = pfind (pid, 0);
      if (!p)
	return POSIX_ESRCH;
      if (p->p_idblock.euid != callerp->p_idblock.euid)
	return POSIX_EPERM;
      *task = p->p_task;
      break;
    }

  return 0;
}

int
check_task_perproc (struct proc *p, 
		    int task_i)
{
  task_t task = task_i;

  if (p->p_task == task)
    return (int)p;
  else
    return 0;
}

error_t
proc_task2pid (struct proc *callerp,
	       task_t task,
	       pid_t *pid)
{
  struct proc *p;
  
  p = (struct proc *) prociterate (check_task_perproc, task);

  /* If it isn't there, we should query the system for live tasks
     and create a pseudo-process for it.  */
  if (!p)
    return POSIX_ESRCH;
  
  *pid = p->p_pid;
  return 0;
}

error_t
proc_task2proc (struct proc *requestor,
		task_t task,
		mach_port_t *port)
{
  struct proc *p;
  
  p = (struct proc *) prociterate (check_task_perproc, task);
  if (!p)
    return POSIX_ESRCH;
  *port = p->p_reqport;
  return POSIX_SUCCESS;
}

		
int
count_tasks (struct proc *p,
	     int foo)
{
  int *data = (int *)foo;
  
  (*data)++;
  return 0;
}

int
store_task (struct proc *p,
	    int foo)
{
  int **data = (int **)foo;
  
  *(*data)++ = p->p_pid;
  return 0;
}

error_t
proc_getallprocs (struct proc *callerp,
		  pid_t *pids,
		  u_int *pids_size)
{
  int nprocs;
  pid_t *loc;
  void *buffer;

  /* Find tasks not in process table.  XXX */
  prociterate (count_tasks, (int)&nprocs);
  if (nprocs <= *pids_size)
    buffer = pids;
  else
    {
      vm_allocate (mach_task_self (), (vm_address_t *) &buffer, 
		   nprocs * sizeof (pid_t), 1);
      *(vm_address_t *)pids = (vm_address_t) buffer;
    }
  loc = buffer;
  prociterate (store_task, (int) &loc);
  *pids_size = nprocs;
  return 0;
}

error_t
proc_getprocinfo (struct proc *callerp,
		  pid_t pid,
		  int **procinfo_p,
		  u_int *procinfo_size)
{
  struct procinfo *pi;
  thread_t *threads;
  u_int nthreads;
  u_int tskbic = TASK_BASIC_INFO_COUNT;
  u_int thdbic = THREAD_BASIC_INFO_COUNT;
  u_int thdsic = THREAD_SCHED_INFO_COUNT;
  struct proc *p;
  int size;
  
  p = pfind (pid, 0);
  
  if (!p)
    return POSIX_ESRCH;
  
  task_threads (p->p_task, &threads, &nthreads);
  
  size = sizeof (struct procinfo) 
    + sizeof (struct thread_basic_info) * nthreads
      + sizeof (struct thread_sched_info) * nthreads;
  vm_allocate (mach_task_self (), (vm_address_t *)&pi, size, 1);

  /* Mark controlling tty */

  if (p->p_stopped)
    pi->pi_state |= PI_STOPPED;
  if (p->p_exec)
    pi->pi_state |= PI_EXECED;
  if (!p->p_sigthread)
    pi->pi_state |= PI_NOSIG;
  if (!p->p_pgrp->pg_orphcnt)
    pi->pi_state |= PI_ORPHAN;
  if (p->p_system)
    pi->pi_state |= PI_SYSTEM;
  if (p == p->p_pgrp->pg_session->s_leader)
    pi->pi_state |= PI_SESSLD;
  if (!p->p_reqport)
    pi->pi_state |= PI_FAKE;

  pi->pi_euid = p->p_idblock.euid;
  pi->pi_ruid = p->p_idblock.ruid;
  pi->pi_ppid = p->p_parent->p_pid;
  pi->pi_pgrp = p->p_pgid;
  pi->pi_session = p->p_pgrp->pg_session->s_leader->p_pid;
  pi->pi_nthreads = nthreads;
  
  task_info (p->p_task, TASK_BASIC_INFO,
	     (task_info_t) &pi->pi_taskinfo, &tskbic);

  while (nthreads--)
    {
      thread_info (threads[nthreads], THREAD_BASIC_INFO, 
		   (thread_info_t) (&pi->pi_threadinfos[nthreads].pis_bi),
		   &thdbic);
      thread_info (threads[nthreads], THREAD_SCHED_INFO,
		   (thread_info_t) &pi->pi_threadinfos[nthreads].pis_si,
		   &thdsic);
      mach_port_deallocate (mach_task_self (), threads[nthreads]);
    }

  vm_deallocate (mach_task_self (), (vm_address_t) threads,
		 nthreads * sizeof (thread_t));
  *procinfo_p = (int *) pi;
  *procinfo_size = size;
  return 0;
}

static error_t
getsomeargs (task_t task,
	     int vecaddr,
	     int *nargsp,
	     char **bufp,
	     u_int *buflenp)
{
  int *arglist;			/* list of arg addresses */
  int arglistlen;
  int narg;			/* which arg we are adding to arglist */
  void *page;			/* pagesize window into proc */
  vm_address_t pageaddr;	/* where the page is */
  u_int pagesize;		/* sigh. */
  error_t err;
  char *cp;
  char *buf;			/* buffer with args */
  char *bp;
  int bufsize;

  arglistlen = 10;
  arglist = malloc (sizeof (int) * arglistlen);
  narg = -1;
  
  pageaddr = trunc_page (vecaddr);
  pagesize = vm_page_size;
  err = vm_read (task, pageaddr, &page, &pagesize);
  if (err)
    {
      free (arglist);
      return POSIX_EFAULT;
    }

  do
    {
      narg++;
      
      /* If we've gone off the end, wrap around. */
      if (vecaddr + narg * sizeof (int) - pageaddr >= vm_page_size)
	{
	  pageaddr += vm_page_size;
	  vm_deallocate (mach_task_self (), page, vm_page_size);
	  err = vm_read (task, pageaddr, &page, &pagesize);
	  if (err)
	    {
	      free (arglist);
	      return POSIX_EFAULT;
	    }
	}

      /* If we can't fit this arg in the array, resize it */
      if (narg > arglistlen)
	{
	  arglistlen *= 2;
	  arglist = realloc (arglist, sizeof (int) * arglistlen);
	}
      
      /* Store this arg */
      arglist[narg] = *(int *)(page + vecaddr + 
			       narg * sizeof (int) - pageaddr);
    }
  while (arglist[narg]);
  
  vm_deallocate (mach_task_self (), page, vm_page_size);
  arglistlen = narg + 1;

  *nargsp = arglistlen;

  vm_allocate (mach_task_self (), &buf, vm_page_size, 1);
  bp = buf;
  bufsize = vm_page_size;
  for (narg = 0; narg < arglistlen; narg++)
    {
      /* Read in the page containing this arg. */
      pageaddr = trunc_page (arglist[narg]);
      err = vm_read (task, pageaddr, &page, &pagesize);
      if (err)
	{
	  free (arglist);
	  return POSIX_EFAULT;
	}

      cp = page + arglist[narg] - pageaddr;
      do
	{
	  if ((int)cp >= (int)page + vm_page_size)
	    {
	      /* Read in the next page */
	      pageaddr += vm_page_size;
	      vm_deallocate (mach_task_self (), page, vm_page_size);
	      err = vm_read (task, pageaddr, &page, &pagesize);
	      if (err)
		{
		  free (arglist);
		  return POSIX_EFAULT;
		}
	      cp = page;
	    }
	  bp++;
	  if (bp - buf > bufsize)
	    {
	      bufsize *= 2;
	      vm_allocate (mach_task_self (), &bp, bufsize, 1);
	      bcopy (buf, bp, bufsize / 2);
	      vm_deallocate (mach_task_self (), buf, bufsize / 2);
	      buf = bp;
	    }
	  *bp = *cp++;
	}
      while (*bp);
    }
  *bufp = buf;
  *buflenp = bufsize;
  free (arglist);
  return POSIX_SUCCESS;
}

error_t
proc_getprocargs (struct proc *callerp,
		  pid_t pid,
		  int *nargsp,
		  char **bufp,
		  u_int *buflenp)
{
  struct proc *p = pfind (pid, 0);
  
  if (!p)
    return POSIX_ESRCH;
  
  return getsomeargs (p->p_task, p->p_argv, nargsp, bufp, buflenp);
}

error_t
proc_getprocenv (struct proc *callerp,
		 pid_t pid,
		 int *nargsp,
		 char **bufp,
		 u_int *buflenp)
{
  struct proc *p = pfind (pid, 0);
  
  if (!p)
    return POSIX_ESRCH;
  
  return getsomeargs (p->p_task, p->p_envp, nargsp, bufp, buflenp);
}


error_t
proc_setprocargs (struct proc *p,
		  int argv,
		  int envp)
{
  p->p_argv = argv;
  p->p_envp = envp;
  return POSIX_SUCCESS;
}

		  
error_t
proc_setlogin (struct proc *p,
	       char *buf,
	       u_int buflen)
{
  if (p->p_idblock.euid)
    return POSIX_EPERM;
  
  if (p->p_login->l_refcnt-- == 0)
    free (p->p_login);
  
  p->p_login = malloc (sizeof (struct login) + buflen);
  p->p_login->l_refcnt = 1;
  bcopy (buf, p->p_login->l_name, buflen);
  p->p_login->l_name[buflen] = '\0'; /* just in case... */
  return POSIX_SUCCESS;
}

error_t
proc_getlogin (struct proc *p,
	       char *buf,
	       u_int *buflen)
{
  bcopy (p->p_login->l_name, buf, *buflen = strlen(p->p_login->l_name) + 1);
  return POSIX_SUCCESS;
}



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 process_server (mach_msg_header_t *, mach_msg_header_t *); /*XXX*/

  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;
	}
      proc_died (inp->msgh_remote_port);
      repl->RetCode = MIG_NO_REPLY;
      return 1;
    }
  return process_server (inp, outp);
}

void
main ()
{
  mach_port_t bootport;
  struct proc *p;
  device_t console;
  extern mach_msg_server ();	/* XXX */
  mach_port_t unused;

  mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_PORT_SET, 
		      &request_portset);

  /* Make the first process */
  task_get_special_port (mach_task_self (), TASK_BOOTSTRAP_PORT, &bootport);
  p = make_process_struct ();

  startup_procinit (bootport, p->p_reqport, &p->p_task, &host_priv,
		    &device_master);
  device_open (device_master, D_WRITE, "console", &console);
  set_console_port (console);

  p->p_pid = genpid ();
  p->p_login = malloc (sizeof (struct login) + 1);
  p->p_login->l_refcnt = 1;
  p->p_login->l_name[0] = '\0';
  
  mach_port_request_notification (mach_task_self (), p->p_task,
				  MACH_NOTIFY_DEAD_NAME, 1, p->p_reqport, 
				  MACH_MSG_TYPE_MAKE_SEND_ONCE, &unused);
  p->p_argv = 0;
  p->p_envp = 0;
  
  p->p_parent = 0;
  p->p_ochild = 0;
  p->p_sib = 0;
  p->p_pgrp = malloc (sizeof (struct pgrp));
  p->p_pgrp->pg_plist = 0;
  p->p_pgrp->pg_pgid = p->p_pid;
  p->p_pgrp->pg_orphcnt = 0;
  p->p_pgrp->pg_session = malloc (sizeof (struct session));
  p->p_pgrp->pg_session->s_leader = p;
  p->p_pgrp->pg_session->s_count = 1;
  p->p_pgid = p->p_pid;
  pgaddhash (p->p_pgrp);
  join_pgrp (p);
  
  bzero (&p->p_idblock, sizeof (struct idblock));
  p->p_sigthread = 0;
  p->p_nsignals = 0;
  p->p_exit_status = 0;
  p->p_stopsig = 0;
  p->p_exec = 0;
  p->p_stopped = 0;
  p->p_system = 1;
  p->p_waited = 0;
  p->p_exited = 0;
  p->p_waiting = 0;
  p->p_traced = 0;
  p->p_nostopcld = 0;
  paddhash (p);

  
  while (1)
    mach_msg_server (request_server, vm_page_size * 2, request_portset);
}


  
