/* Utility subroutines for process 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.  */

#include "proc.h"
#include "process.h"
#include "process_reply.h"
#include "misc.h"

#include <mach/msg_type.h>

/* Send a signal SIG to process P.  Return true if the process will be
   stopped as a result of the signal.  */
int
psig (struct proc *p,
      int sig,
      int cttykill)
{
  int willstop;
  
  assert (sig && sig < NSIG);

  p->p_nsignals++;

  /* XXX special SIGKILL processing here for "reluctant" tasks.
     (except p_system tasks).  */

  sig_post (p->p_sigthread, sig, !p->p_pgrp->pg_orphcnt, cttykill, &willstop);

  return willstop;
}    


/* Continuation for wait */
void
wait_continuation (struct proc *p,
		   int interrupt)
{
  struct rusage ru;
  int waitstatus;
  pid_t childpid;
  error_t err;

  p->p_waiting = 0;

  if (interrupt)
    err = POSIX_EINTR;
  else
    err = proc_wait (p, p->p_wc.pw_reply_port, p->p_wc.pw_reply_port_type,
		     p->p_wc.pw_pid, &waitstatus, p->p_wc.pw_options, &ru,
		     &childpid);
  if (err != MIG_NO_REPLY)
    proc_wait_reply (p->p_wc.pw_reply_port, p->p_wc.pw_reply_port_type,
		     err, waitstatus, ru, childpid);
}



/* Mark the process as a member of its process group.  */
void
join_pgrp (struct proc *p)
{
  struct proc *ip;
  
  p->p_next = p->p_pgrp->pg_plist;
  if (p->p_pgrp->pg_plist)
    p->p_pgrp->pg_plist->p_prevp = &p->p_next;
  
  p->p_pgrp->pg_plist = p;
  p->p_prevp = &p->p_pgrp->pg_plist;

  if (p->p_parent->p_pgrp != p->p_pgrp
      && p->p_parent->p_pgrp->pg_session == p->p_pgrp->pg_session)
    {
      if (p->p_pgrp->pg_orphcnt == 0)
	for (ip = p->p_pgrp->pg_plist; ip; ip = ip->p_next)
	  proc_newids (ip->p_sigthread, ip->p_pid, ip->p_pgrp->pg_pgid, 0);
      p->p_pgrp->pg_orphcnt++;
    }
}


/* Remove a process from its process group's data structures.  */
void
leave_pgrp (struct proc *p)
{
  int dosignal;
  struct proc *ip;
  
  if (p->p_next)
    p->p_next->p_prevp = p->p_prevp;
  *p->p_prevp = p->p_next;
  
  if (p->p_pgrp->pg_plist == 0)
    {
      pgremhash (p->p_pgrp);
      if (!p->p_pgrp->pg_session->s_count--)
	free (p->p_pgrp->pg_session);
      free (p->p_pgrp);
      return;
    }
  
  if (p->p_parent->p_pgrp != p->p_pgrp
      && p->p_parent->p_pgrp->pg_session == p->p_pgrp->pg_session)
    {
      p->p_pgrp->pg_orphcnt--;
  
      assert (p->p_pgrp->pg_orphcnt >= 0);
  
      if (!p->p_pgrp->pg_orphcnt)
	{
	  dosignal = 0;
  
	  for (ip = p->p_pgrp->pg_plist; ip; ip = ip->p_next)
	    {
	      if (ip->p_stopped)
		dosignal = 1;
	      proc_newids (ip->p_sigthread, ip->p_pid, ip->p_pgrp->pg_pgid, 1);
	    }
	  if (dosignal)
	    for (ip = p->p_pgrp->pg_plist; ip; ip = ip->p_next)
	      {
		psig (ip, SIGHUP, 0);
		psig (ip, SIGCONT, 0);
	      }
	}
    }
}


/* Hash list of processes */
#define NPROCHASH 511
#define PID_MAX 30000
static struct proc *phash[NPROCHASH];

struct proc *
pfind (pid_t pid,
       int dead)
{
  struct proc *p;
  
  for (p = phash[pid % NPROCHASH]; p; p = p->p_hnext)
    if (p->p_pid == pid)
      {
	if (!p->p_exited || dead)
	  return p;
	else
	  return 0;
      }
  
  return 0;
}

void
paddhash (struct proc *p)
{
  int bucket = p->p_pid % NPROCHASH;
  
  p->p_hnext = phash[bucket];
  if (phash[bucket])
    phash[bucket]->p_hprevp = &p->p_hnext;
  p->p_hprevp = &phash[bucket];
  phash[bucket] = p;
}

int
prociterate (int (*fun)(struct proc *, int), int arg)
{
  int bucket;
  struct proc *p;
  int out;
  
  for (bucket = 0; bucket < NPROCHASH; bucket++)
    for (p = phash[bucket]; p; p = p->p_hnext)
      if (!p->p_exited && (out = (*fun) (p, arg)))
	return out;
  return 0;
}


void
premhash (struct proc *p)
{
  if (p->p_hnext)
    p->p_hnext->p_hprevp = p->p_hprevp;
  *p->p_hprevp = p->p_hnext;
}

static pid_t mpid = 0;

pid_t
genpid ()
{
  pid_t newpid;

  do
    {
      newpid = mpid++;
      if (mpid > PID_MAX)
	mpid = 300;
    }
  while (!pfind (newpid, 1) && !pgfind (newpid));
  return newpid;
}


/* Hash list of process groups */
#define NPGRPHASH 63
static struct pgrp *pghash[NPGRPHASH];

struct pgrp *
pgfind (pid_t pgrp)
{
  struct pgrp *pg;
  
  for (pg = pghash[pgrp % NPGRPHASH]; pg; pg = pg->pg_hnext)
    if (pg->pg_pgid == pgrp)
      return pg;
  return 0;
}

void
pgaddhash (struct pgrp *pg)
{
  int bucket = pg->pg_pgid % NPGRPHASH;
  
  pg->pg_hnext = pghash[bucket];
  if (pghash[bucket])
    pghash[bucket]->pg_hprevp = &pg->pg_hnext;
  pg->pg_hprevp = &pghash[bucket];
  pghash[bucket] = pg;
}

void
pgremhash (struct pgrp *pg)
{
  if (pg->pg_hnext)
    pg->pg_hnext->pg_hprevp = pg->pg_hprevp;
  *pg->pg_hprevp = pg->pg_hnext;
}

struct proc *
make_process_struct ()
{
  struct proc *p;
  error_t err;
  
  p = malloc (sizeof (struct proc));
  err = mach_port_allocate_name (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
				 (mach_port_t) p);
  if (err == KERN_NAME_EXISTS)
    {
      struct proc *newp;
      newp = make_process_struct ();
      free (p);
      return newp;
    }
  p->p_reqport = (mach_port_t) p;
  mach_port_move_member (mach_task_self (), p->p_reqport, request_portset);
  return p;
}

struct proc *
convert_port_to_pstruct (mach_port_t pt)
{
  return (struct proc *) pt;
}
