/* iswait.c
   Wait for a process to finish.

   Copyright (C) 1992 Ian Lance Taylor

   This file is part of the Taylor UUCP package.

   This program 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 of the
   License, or (at your option) any later version.

   This program 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 this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

   The author of the program may be contacted at ian@airs.com or
   c/o Cygnus Support, 48 Grove Street, Somerville, MA 02144.
   */

#include "uucp.h"

#include "uudefs.h"
#include "sysdep.h"

#include <errno.h>

#if HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif

/* We use a typedef wait_status for wait (waitpid, wait4) to put
   results into.  We define the POSIX examination functions we need if
   they are not already defined (if they aren't defined, I assume that
   we have a standard wait status).  */

#if HAVE_UNION_WAIT
typedef union wait wait_status;
#ifndef WIFEXITED
#define WIFEXITED(u) ((u).w_termsig == 0)
#endif
#ifndef WEXITSTATUS
#define WEXITSTATUS(u) ((u).w_retcode)
#endif
#ifndef WTERMSIG
#define WTERMSIG(u) ((u).w_termsig)
#endif
#else /* ! HAVE_UNION_WAIT */
typedef int wait_status;
#ifndef WIFEXITED
#define WIFEXITED(i) (((i) & 0xff) == 0)
#endif
#ifndef WEXITSTATUS
#define WEXITSTATUS(i) (((i) >> 8) & 0xff)
#endif
#ifndef WTERMSIG
#define WTERMSIG(i) ((i) & 0x7f)
#endif
#endif /* ! HAVE_UNION_WAIT */

/* Wait for a particular process to finish.  The ipid argument should
   be pid_t, but then we couldn't have a prototype.  If the zreport
   argument is not NULL, then a wait error will be logged, and if the
   exit status is non-zero it will be logged with zreport as the
   header of the log message.  If the zreport argument is NULL, no
   errors will be logged.  This function returns the exit status if
   the process exited normally, or -1 on error or if the process was
   killed by a signal (I don't just always return the exit status
   because then the calling code would have to prepared to handle
   union wait status vs. int status, and none of the callers care
   which signal killed the program anyhow).

   This functions keeps waiting until the process finished, even if it
   is interrupted by a signal.  I think this is right for all uses.
   The controversial one would be when called from uuxqt to wait for a
   requested process.  Hitting uuxqt with SIGKILL will approximate the
   actions taken if we return from here with an error anyhow.  If we
   do get a signal, we call ulog with a NULL argument to get it in the
   log file at about the right time.  */

int
ixswait (ipid, zreport)
     unsigned long ipid;
     const char *zreport;
{
  wait_status istat;

#if HAVE_WAITPID
  while (waitpid ((pid_t) ipid, (pointer) &istat, 0) < 0)
    {
      if (errno != EINTR)
	{
	  if (zreport != NULL)
	    ulog (LOG_ERROR, "waitpid: %s", strerror (errno));
	  return -1;
	}
      ulog (LOG_ERROR, (const char *) NULL);
    }
#else /* ! HAVE_WAITPID */
#if HAVE_WAIT4
  while (wait4 ((pid_t) ipid, (pointer) &istat, 0,
		(struct rusage *) NULL) < 0)
    {
      if (errno != EINTR)
	{
	  if (zreport != NULL)
	    ulog (LOG_ERROR, "wait4: %s", strerror (errno));
	  return -1;
	}
      ulog (LOG_ERROR, (const char *) NULL);
    }
#else /* ! HAVE_WAIT4 */
  pid_t igot;

  /* We could theoretically get the wrong child here if we're in some
     kind of weird pipeline, so we don't give any error messages for
     it.  */
  while ((igot = wait ((pointer) &istat)) != (pid_t) ipid)
    {
      if (igot < 0)
	{
	  if (errno != EINTR)
	    {
	      if (zreport != NULL)
		ulog (LOG_ERROR, "wait: %s", strerror (errno));
	      return -1;
	    }
	  ulog (LOG_ERROR, (const char *) NULL);
	}
    }
#endif /* ! HAVE_WAIT4 */
#endif /* ! HAVE_WAITPID */  

  DEBUG_MESSAGE2 (DEBUG_EXECUTE, "%s %d",
		  WIFEXITED (istat) ? "Exit status" : "Signal",
		  WIFEXITED (istat) ? WEXITSTATUS (istat) : WTERMSIG (istat));

  if (WIFEXITED (istat) && WEXITSTATUS (istat) == 0)
    return 0;

  if (zreport != NULL)
    {
      if (! WIFEXITED (istat))
	ulog (LOG_ERROR, "%s: Got signal %d", zreport, WTERMSIG (istat));
      else
	ulog (LOG_ERROR, "%s: Exit status %d", zreport,
	      WEXITSTATUS (istat));
    }

  if (WIFEXITED (istat))
    return WEXITSTATUS (istat);
  else
    return -1;
}
