/* Report on what a thread in our task is waiting for.
   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include <hurd.h>
#include <hurd/signal.h>
#include <hurd/fd.h>
#include <string.h>
#include <assert.h>
#include <hurd/msg_server.h>
#include "thread_state.h"
#include "intr-msg.h"

static void
describe_number (string_t description, const char *flavor, unsigned long int i)
{
  unsigned long int j;
  char *p = __stpcpy (description, flavor);

  /* Allocate space for the number at the end of DESCRIPTION.  */
  for (j = i; j >= 10; j /= 10)
    p++;
  p[1] = '\0';

  do
    {
      *p-- = '0' + i % 10;
      i /= 10;
    } while (i != 0);
  assert (*p == '#');
}

static void
describe_port (string_t description, mach_port_t port)
{
  int i;

  if (port == __mach_task_self ())
    {
      strcpy (description, "task-self");
      return;
    }

  for (i = 0; i < _hurd_nports; ++i)
    if (port == _hurd_ports[i].port)
      {
	describe_number (description, "init#", i);
	return;
      }

  if (_hurd_init_dtable)
    {
      for (i = 0; i < _hurd_init_dtablesize; ++i)
	if (port == _hurd_init_dtable[i])
	  {
	    describe_number (description, "fd#", i);
	    return;
	  }
    }
  else if (_hurd_dtable)
    {
      for (i = 0; i < _hurd_dtablesize; ++i)
	if (_hurd_dtable[i] == NULL)
	  continue;
	else if (port == _hurd_dtable[i]->port.port)
	  {
	    describe_number (description, "fd#", i);
	    return;
	  }
	else if (port == _hurd_dtable[i]->ctty.port)
	  {
	    describe_number (description, "bgfd#", i);
	    return;
	  }
    }

  describe_number (description, "port#", port);
}


/* We want _HURD_ITIMER_THREAD, but don't want to link in the itimer code
   unnecessarily.  */
#if 0 /* libc.so.0.0 needs this defined, so make it a weak alias for now.  */
extern thread_t _hurd_itimer_thread; /* XXX */
weak_extern (_hurd_itimer_thread)
#else
static thread_t default_hurd_itimer_thread;
weak_alias (default_hurd_itimer_thread, _hurd_itimer_thread)
#endif

kern_return_t
_S_msg_report_wait (mach_port_t msgport, thread_t thread,
		    string_t description, int *msgid)
{
  *msgid = 0;

  if (thread == _hurd_msgport_thread)
    /* Cute.  */
    strcpy (description, "msgport");
  else if (&_hurd_msgport_thread && thread == _hurd_itimer_thread)
    strcpy (description, "itimer");
  else
    {
      /* Make sure this is really one of our threads.  */

      struct hurd_sigstate *ss;

      __mutex_lock (&_hurd_siglock);
      for (ss = _hurd_sigstates; ss != NULL; ss = ss->next)
	if (ss->thread == thread)
	  break;
      __mutex_unlock (&_hurd_siglock);
      if (ss == NULL)
	/* To hell with you.  */
	return EINVAL;

      if (ss->suspended != MACH_PORT_NULL)
	strcpy (description, "sigsuspend");
      else
	{
	  /* Examine the thread's state to see if it is blocked in an RPC.  */

	  struct machine_thread_state state;
	  mach_msg_type_number_t count = MACHINE_THREAD_STATE_COUNT;
	  error_t err;

	  err = __thread_get_state (thread, MACHINE_THREAD_STATE_FLAVOR,
				    (integer_t *) &state, &count);
	  if (err)
	    return err;
	  assert (count == MACHINE_THREAD_STATE_COUNT);
	  if (SYSCALL_EXAMINE (&state, msgid))
	    {
	      /* Blocked in a system call.  */
	      if (*msgid == -25)
		/* mach_msg system call.  Examine its parameters.  */
		describe_port (description, MSG_EXAMINE (&state, msgid));
	      else
		strcpy (description, "kernel");
	    }
	  else
	    description[0] = '\0';
	}
    }

  __mach_port_deallocate (__mach_task_self (), thread);
  return 0;
}

kern_return_t
_S_msg_describe_ports (mach_port_t msgport, mach_port_t refport,
		       mach_port_t *ports, mach_msg_type_number_t nports,
		       char **desc, mach_msg_type_number_t *desclen)
{
  char *p, *end;

  if (__USEPORT (AUTH, msgport != port))
    return EPERM;

  end = *desc + *desclen;
  p = *desc;
  while (nports-- > 0)
    {
      char this[200];
      describe_port (this, *ports++);
      p = __stpncpy (p, this, end - p);
      if (p == end && p[-1] != '\0')
	return ENOMEM;
    }

  *desclen = p - *desc;
  return 0;
}
