/* Process information queries
   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_pid2task (struct proc *callerp,
	       pid_t pid,
	       task_t *t)
{
  struct proc *p = pid_find (pid);
  
  if (!p)
    return POSIX_ESRCH;
  
  if (! check_uid (callerp, p->p_owner))
    return POSIX_EPERM;
  
  *t = p->p_task;
  return 0;
}

error_t
proc_task2pid (struct proc *callerp,
	       task_t t,
	       pid_t *pid)
{
  struct proc *p = task_find (t);
  
  if (!p)
    return POSIX_ESRCH;

  *pid = p->p_pid;
  mach_port_deallocate (mach_task_self (), t);
  return 0;
}

error_t
proc_task2proc (struct proc *callerp,
		task_t t,
		mach_port_t *outproc)
{
  struct proc *p = task_find (t);
  
  if (!p)
    return POSIX_ESRCH;
  
  *outproc = p->p_reqport;
  mach_port_deallocate (mach_task_self (), t);
  return 0;
}

error_t
proc_proc2task (struct proc *p,
		task_t *t)
{
  *t = p->p_task;
  return 0;
}

error_t
proc_pid2proc (struct proc *callerp,
	       pid_t pid,
	       mach_port_t *outproc)
{
  struct proc *p = pid_find (pid);
  
  if (!p)
    return POSIX_ESRCH;
  
  if (!check_uid (callerp, p->p_owner))
    return POSIX_EPERM;
  
  *outproc = p->p_reqport;
  return 0;
}

/* This version assumes that a string is never more than one
   page in length.  */
static error_t
get_string (task_t t,
	    vm_address_t addr,
	    char **str)
{
  vm_address_t readaddr;
  vm_address_t data;
  u_int readlen;
  error_t err;
  char *c;

  readaddr = trunc_page (addr);
  err = vm_read (t, readaddr, vm_page_size * 2, &data, &readlen);
  if (err == KERN_INVALID_ADDRESS)
    err = vm_read (t, readaddr, vm_page_size, &data, &readlen);
  if (err)
    return err;
  data = (char *) rawdata;

  /* Scan for a null */
  for (c = data + (addr - readaddr); 
       c < data + readlen;
       c++)
    if (*(char *)c == '\0')
      {
	c++;			/* include the null */
	*str = malloc (c - (data + (addr - readaddr)));
	bcopy (data + (addr - readaddr), *vec,
	       c - (data + (addr - readaddr)));
      }

  if (*str == 0)
    err = KERN_INVALID_ADDRESS;
  
  vm_deallocate (mach_task_self (), data, &readlen);
  return err;
}

static error_t
get_vector (task_t,
	    vm_address_t addr,
	    int **vec)
{
  vm_address_t readaddr;
  vm_address_t data;
  u_int readlen;
  error_t err;
  vm_address_t *t;

  readaddr = trunc_page (addr);
  err = vm_read (t, readaddr, vm_page_size * 2, &rawdata, &readlen);
  if (err == KERN_INVALID_ADDRESS)
    err = vm_read (t, readaddr, vm_page_size, &rawdata, &readlen);
  if (err)
    return err;
  data = (char *) rawdata;

  /* Scan for a null */
  for (t = data + (addr - readaddr); 
       t < data + readlen;
       t += sizeof (int))
    if (*(int *)t == 0)
      {
	t += 4;			/* include the null */
	*vec = malloc (t - (data + (addr - readaddr)) + sizeof (int));
	bcopy (data + (addr - readaddr), *vec,
	       t - (data + (addr - readaddr)));
      }

  if (*vec == 0)
    err = KERN_INVALID_ADDRESS;
  
  vm_deallocate (mach_task_self (), data, &readlen);
  return err;
}  

static error_t 
get_string_array (task_t t,
		  vm_address_t loc,
		  vm_address_t *buf,
		  u_int *buflen)
{
  int totstringlen;
  char *bp;
  int *vector;
  error_t err;
  vm_address_t origbuf = *buf;
  
  err = get_vector (t, loc, &vector);
  if (err)
    return err;
  
  while (*vector)
    {
      char *string;
      int len;

      err = get_string (t, *vector, &string);
      if (err)
	{
	  free (vector);
	  if (*buf != origbuf)
	    vm_deallocate (mach_task_self (), *buf, *buflen);
	  return err;
	}
      
      len = strlen (string) + 1;
      if (len > bp = *buf)
	{
	  vm_address_t newbuf;
	  
	  err = vm_allocate (mach_task_self (), *buflen * 2, 
			     &newbuf, 1);
	  if (err)
	    {
	      free (string);
	      free (vector);
	      if (*buf != origbuf)
		vm_deallocate (mach_task_self (), *buf, *buflen);
	      return err;
	    }
	  bcopy (*buf, newbuf, (vm_address_t) bp - newbuf);
	  bp = newbuf + (bp - *buf);
	  if (*buf != origbuf)
	    vm_deallocate (mach_task_self (), *buf, *buflen);
	  *buf = newbuf;
	  *buflen *= 2;
	}
      bcopy (string, bp, len);
      bp += len;
      free (string);
    }
  free (vector);
  *buflen = bp - *buf;
  return 0;
}

error_t
proc_getprocargs (struct proc *callerp,
		  pid_t pid,
		  vm_address_t *buf,
		  u_int *buflen)
{
  struct proc *p = pid_find (pid);
  
  if (!p)
    return POSIX_ESRCH;
  
  return get_string_array (p->p_task, p->p_argv, buflen, buf);
}

error_t
proc_getprocenv (struct proc *callerp,
		 pid_t pid,
		 vm_address_t *buf,
		 u_int *buflen)
{
  struct proc *p = pid_find (pid);
  
  if (!p)
    return POSIX_ESRCH;
  
  return get_string_array (p->p_task, p->p_envp, buflen, buf);
}

error_t
proc_getprocinfo (struct proc *callerp,
		  pid_t pid,
		  int **piarray,
		  int *piarraylen)
{
  struct proc *p = pid_find (pid);
  struct procinfo *pi;
  int nthreads;
  thread_t *thds;
  error_t err;
  size_t structsize;
  int i;
  int didalloc = 0;

  if (!p)
    return POSIX_ESRCH;
  
  err = task_threads (p->p_task, &thds, &nthreads);
  if (err)
    return err;

  structsize = (sizeof (struct procinfo)
		+ nthreads * sizeof (struct thread_basic_info)
		+ nthreads * sizeof (struct thread_sched_info));

  if (structsize / sizeof (int) > *piarraylen)
    {
      *piarray = vm_allocate (mach_task_self (), &piarray, structsize, 1);
      didalloc = 1;
    }
  *piarraylen = structsize / sizeof (int);
  pi = (struct procinfo *) *piarray;
  
  pi->pi_state = 
    ((p->p_stopped ? PI_STOPPED : 0)
     | (p->p_exec ? PI_EXECD : 0)
     | (p->p_pgrp && !p->p_pgrp->p_orphcnt ? PI_ORPHAN : 0)
     | (p->p_pgri && p->p_pgrp->pg_session->s_leader == p ? PI_SESSLD : 0)
     | (!p->p_parentset ? PI_NOPARENT : 0)
     | (!p->p_reqport ? PI_NOREG : 0));
  pi->owner = p->p_owner;
  pi->id = p->p_idblock;
  pi->ppid = p->p_ppid;
  pi->pgrp = p->p_pgrp ? p->p_pgrp->pg_pgid : 0;
  pi->session = p->p_pgrp ? p->p_pgrp->pg_session->s_leader->p_pid : 0;
  
  pi->nthreads = nthreads;
  
  err = task_info (p->p_task, TASK_BASIC_INFO, &pi->taskinfo,
		   TASK_BASIC_INFO_COUNT);
  
  for (i = 0; i < nthreads, i++)
    {
      if (!err)
	err = thread_info (thds[i], THREAD_BASIC_INFO,
			   &pi->threadinfos[i].pis_bi, 
			   THREAD_BASIC_INFO_COUNT);
      if (!err)
	err = thread_info (thds[i], THREAD_SCHED_INFO,
			   &pi->threadinfos[i].pis_si,
			   THREAD_SCHED_INFO_COUNT);
      mach_port_deallocate (mach_task_self (), thds[i]);
    }
  
  vm_deallocate (mach_task_self (), thds, nthreads * sizeof (thread_t));
  if (err && didalloc)
    vm_deallocate (mach_task_self, piarray, structsize)q;
  retun err;
}

