/* Process management
   Copyright (C) 1992 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 2, 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.  */

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

error_t
proc_child (struct proc *parentp,
	    task_t childt)
{
  struct proc *childp = task_lookup (childt);

  if (!childp)
    return ESRCH;
  
  if (childp->p->parentset)
    return EBUSY;
  
  childp->p_login = parentp->p_login;
  childp->p_login->l_refcnt++;
  childp->p_owner = parentp->p_owner;
  childp->p_idblock = parentp->p_idblock;
  
  childp->p_parent = parentp;
  childp->p_sib = parentp->p_ochild;
  childp->p_prevsib = &parentp->p_ochild;
  if (parentp->p_ochild)
    parentp->p_ochild->p_prevsib = &childp->p_sib;
  parentp->p_ochild = childp;
  
  childp->p_pgrp = parentp->p_pgrp;

  join_pgrp (childp)

  childp->p_parentset = 1;
  return 0;
}

error_t
proc_setmsgport (struct proc *p,
		 mach_port_t msgport,
		 mach_port_t *oldmsgport)
{
  *oldmsgport = p->p_msgport;
  p->p_msgport = msgport;
  return 0;
}

error_t
proc_reassign (struct proc *oldp,
	       task_t newt)
{
  struct proc *newp = task_lookup (newt);
  struct proc *tp;
  task_t oldt = oldp->p_task;

  if (!newp)
    return ESRCH;
  
  if (newp->p_parentset
      || newp->p_login
      || newp->p_parent
      || newp->p_ochild
      || newp->p_sib
      || newp->p_pgrp
      || newp->p_msgport)
    return EBUSY;
  
  /* Copy all relevant state. */
  newp->p_pid = oldp->p_pid;
  add_hash (&pid_hash, newp, &newp->p_pidhashloc, newp->p_pid);
  newp->p_login = oldp->p_login;
  if (newp->p_login)
    ++newp->p_login->l_refcnt;
  newp->p_owner = oldp->p_owner;
  newp->p_idblock = oldp->p_idblock;

  newp->p_parent = oldp->p_parent;
  newp->p_ochild = oldp->p_ochild;
  for (tp = newp->p_ochild; tp = tp->p_sib; tp)
    tp->p_parent = newp;
  if (newp->p_parent)
    {
      newp->p_sib = newp->p_parent->p_ochild;
      newp->p_prevsib = &newp->p_parent->p_ochild;
      if (newp->p_sib)
	newp->p_sib->p_prevsib = &newp->p_sib;
    }

  newp->p_pgrp = oldp->p_pgrp;
  newp->p_msgport = oldp->p_msgport;
  oldp->p_msgport = 0;		/* prevent its deallocation */

  newp->p_argv = newp->p_envp = newp->p_status = 0;
  newp->p_exec = oldp->p_exec;
  newp->p_stopped = oldp->p_stopped;
  newp->p_waited = oldp->p_waited;
  newp->p_exiting = oldp->p_exiting;
  newp->p_exited = oldp->p_exited;
  newp->p_traced = oldp->p_traced;
  newp->p_nostopcld = oldp->p_nostopcld;
  newp->p_parentset = oldp->p_parentset;
  newp->p_reassigned = 0;

  leave_pgrp (oldp);
  proc_destroy (oldp, 1);

  join_pgrp (newp);

  return 0;
}

error_t
proc_setowner (struct proc *p,
	       uid_t owner)
{
  if (! check_uid (p, owner))
    return POSIX_EPERM;
  
  p->p_owner = owner;
  return 0;
}

error_t
proc_getpids (struct proc *p,
	      pid_t *pid,
	      pid_t *ppid,
	      int *orphaned)
{
  *pid = p->p_pid;
  *ppid = p->p_ppid;
  if (p->p_pgrp)
    *orphaned = !p->p_pgrp->pg_orphcnt;
  else
    *orphaned = 0;		/* XXX */
  return 0;
}

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

error_t
proc_getmsgport (struct proc *p,
		 int pid,
		 mach_port_t *msgport)
{
  struct proc *p = pid_find (pid);
  
  if (!p)
    return ESRCH;
  
  *msgport = p->p_p_msgport;
  return 0;
}

error_t
proc_wait (struct proc *callerp,
	   mach_port_t reply_port,
	   mach_msg_type_name_t reply_port_type,
	   int pid,
	   int options,
	   int *status,
	   struct rusage *ru,
	   int *pid_status)
{
  struct proc *p;
  struct pgrp *pg;
  int found;
  
#define waitable(p) ((p)->p_exited 					    \
		     || ((p)->p_stopped && !(p)->p_waited		    \
			 && ((p)->p_traced || (options & WUNTRACED))))

  if (pid == 0)
    {
      if (!callerp->p_ochild)
	return ESRCH;
      
      for (p = callerp->p_ochild; p; p->p_sib)
	if (waitable (p))
	  goto gotit;
    }
  else if (pid < 0)
    {
      pg = pgrp_find (-pid);
      if (!pg)
	return ESRCH;

      found = 0;
      for (p = callerp->p_ochild; p; p->p_sib)
	if (p->p_pgrp == pg)
	  {
	    found = 1;
	    if (waitable (p))
	      goto gotit;
	  }
      if (!found)
	return ESRCH;
    }
  else
    {
      p = pid_find (pid);
      if (!p || p->p_parent != callerp)
	return ESRCH;
      if (waitable (p))
	goto gotit;
    }

  if (options & WNOHANG)
    return 0;
  
  /* We have a running child of the right sort.  Save the info 
     for a later exit. */

  callerp->p_wc.pw_reply_port = reply_port;
  callerp->p_wc.pw_reply_port_type = reply_port_type;
  callerp->p_wc.pw_pid = pid;
  callerp->p_wc.pw_options = options;
  callerp->p_waiting = 1;
  return MIG_NO_REPLY;

 gotit:
  bzero (ru, sizeof (struct ru));
  *status = p->p_status;
  *pid_status = p->p_pid;
  if (p->p_exited)
    free_process (p);
  else
    p->p_waited = 1;
  return 0;
}

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]);
      }
  task_resume (p->p_task);
  return 0;
}

struct proc *
pid_find (pid_t pid)
{
  struct proc *p;

  p = findhash (&pidhash, pid);
  if (p && p->p_reassigned)
    panic ("reassigned process found in pid hash table");
  return p;
}

struct proc *
task_find (task_t task)
{
  struct proc *p;
  
  p = findhash (&taskhash, pid);

  if (!p)
    p = new_proc (task);

  if (p && p->p_reassigned)
    p = 0;

  return p;
}

struct proc *
new_proc (task_t task)
{
  struct proc *p;
  
  if (!valid_task (task))
    return 0;
  
  p = malloc (sizeof (struct proc));
  p->p_gnext = 0;
  p->p_pid = genpid ();
  p->p_task = task;
  
  p->p_login = 0;
  p->p_owner = 0;
  p->p_nuids = 0;
  p->p_ngids = 0;
  
  p->p_parent = 0;
  p->p_ochild = 0;
  p->p_sib = 0;
  
  p->p_pgrp = 0;
 
  mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
		      &p->p_reqport);
  mach_port_move_member (mach_task_self (), p->p_reqport, proc_portset);
  p->p_msgport = MACH_PORT_NULL;
  
  p->p_wc.pw_reply_port = MACH_PORT_NULL;
  
  p->p_argv = p->p_envp = p->p_status = 0;
  
  p->p_exec = 0;
  p->p_stopped = 0;
  p->p_waited = 0;
  p->p_exiting = 0;
  p->p_exited = 0;
  p->p_waiting = 0;
  p->p_traced = 0;
  p->p_nostopcld = 0;
  p->p_parentset = 0;
  p->p_reassigned = 0;

  addhash (&pid_hash, p, &p->p_pidhashloc, p->p_pid);
  addhash (&task_hash, p, &p->p_taskhashloc, p->p_task);
}


/* A process is going away.  Destroy everything.  If keeparound is
   set, then don't actually delete the process struct itself; instead,
   mark p_reassigned, leave it on the by-task hash table, and destroy
   everything else.  Before making this call, the process should
   already have been removed from its pgrp.  */
void
proc_destroy (struct proc *p, int keeparound)
{
  *p->p_pidhashloc = HASH_DEL;
  if (!keeparound)
    *p->p_taskhashloc = HASH_DEL;
  
  if (--p->p_login->l_refcnt)
    free (p->p_login);
  
  if (p->p_parentset)
    *p->p_prevsib = p->p_sib;
  
  mach_port_mod_refs (mach_task_self (), p->p_reqport, 
		      MACH_PORT_RIGHT_RECEIVE, -1);
  if (p->p_msgport)
    mach_port_deallocate (mach_task_self (), p->p_msgport);
  
  if (p->p_wc->pw_reply_port)
    mach_port_deallocate (mach_task_self (), p->p_wc->pw_reply_port);
  
  if (keeparound)
    p->p_reassigned = 1;
  else
    free (p);
}


		      
