/* Miscellaneous utility routines for GNATS.
   Copyright (C) 1993 Free Software Foundation, Inc.
   Contributed by Tim Wicinski (wicinski@barn.com).

This file is part of GNU GNATS.

GNU GNATS 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.

GNU GNATS 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 GNU GNATS; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "config.h"

#if defined(__STDC__) || defined(_AIX)
#include <stdarg.h>
#define VA_START(args, lastarg) va_start(args, lastarg)
#else
#include <varargs.h>
#define VA_START(args, lastarg) va_start(args)
#endif

#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif

#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "gnats.h"
#include "globals.h"
#include "pathmax.h"

/* take a link list of index entries and write it back out... */
void
write_index (index_start)
     Index *index_start;
{

  FILE *fp;
  char *path = (char *) xmalloc (PATH_MAX);
  char *tmpfile = (char *) xmalloc (PATH_MAX);
  Index *i;

  sprintf (tmpfile, "%s/gnats-adm/indXXXXXX", gnats_root);
  sprintf (path, "%s/gnats-adm/%s", gnats_root, INDEX);
  mktemp (tmpfile);

  fp = fopen (tmpfile, "w");
  if (fp == NULL)
    {
      fprintf (stderr, "%s: can't open the temporary file %s\n",
               program_name, tmpfile);
      return;
    }

  for (i = index_start; i ; i = i->next) 
    {
      fprintf(fp, "%s/%s:%s:%s:%s:%s:%s:%s\n", i->category, i->number,
        i->submitter, i->responsible, i->state, i->confidential,
        i->severity, i->priority);
    }

  fclose (fp);

  block_signals ();

  if ((rename (tmpfile, path)) < 0)
    {
      if (errno != EXDEV)
	punt (1, "%s: could not rename temporary index %s into gnats-adm: %s\n",
	      program_name, tmpfile, strerror (errno));

      if (copy_file (tmpfile, path))
	punt (1, "%s: could not copy temporary index %s into %s: %s\n",
	      program_name, tmpfile, path, strerror (errno));

      unlink (tmpfile);
    }

  unblock_signals (); 
}

/* Add the current PR to the GNATS index file.  */
void
add_to_index ()
{
  FILE *fp;
  char *path = (char *) xmalloc (PATH_MAX);
  char *buf = (char *) xmalloc (STR_MAX);

  block_signals ();

  sprintf (path, "%s/gnats-adm/%s", gnats_root, INDEX); 

  fp = fopen (path, "a+");
  if (fp == NULL) 
    punt (1, "Can't append to the GNATS index file (%s).", path);

  create_index_entry (buf);
  fputs (buf, fp);

  fclose (fp);
  unblock_signals ();
}

/* Copy regular file SOURCE onto file DEST.
   Return 1 if an error occurred, 0 if successful. */

int
copy_file (source, dest)
     char *source, *dest;
{
  int ifd;
  int ofd;
  char buf[1024 * 8];
  int len;			/* Number of bytes read into `buf'. */
  
  if (unlink (dest) && errno != ENOENT)
    {
      log_msg (LOG_INFO, 1, "cannot remove ", dest);
      return 1;
    }

  ifd = open (source, O_RDONLY, 0);
  if (ifd < 0)
    {
      log_msg (LOG_INFO, 1, "can not open: ", source);
      return 1;
    }
  ofd = open (dest, O_WRONLY | O_CREAT | O_TRUNC, 0600);
  if (ofd < 0)
    {
      log_msg (LOG_INFO, 1, "can not open: ", dest);
      close (ifd);
      return 1;
    }

  while ((len = read (ifd, buf, sizeof (buf))) > 0)
    {
      int wrote = 0;
      char *bp = buf;
      
      do
	{
	  wrote = write (ofd, bp, len);
	  if (wrote < 0)
	    {
	      log_msg (LOG_INFO, 1, "error in writing:", dest);
	      close (ifd);
	      close (ofd);
	      unlink (dest);
	      return 1;
	    }
	  bp += wrote;
	  len -= wrote;
	} while (len > 0);
    }
  if (len < 0)
    {
      log_msg (LOG_INFO, 1, source, strerror(errno));
      close (ifd);
      close (ofd);
      unlink (dest);
      return 1;
    }

  if (close (ifd) < 0)
    {
      log_msg (LOG_INFO, 1, source, strerror(errno));
      close (ofd);
      return 1;
    }
  if (close (ofd) < 0)
    {
      log_msg (LOG_INFO, 1, dest, strerror(errno));
      return 1;
    }

  /* FIXME: This is fine for GNATS, but if there's anything we ever want
     to have special permissions, we must add the code to restore the
     permissions to what they were.  */
  chmod (dest, 0644);
  
  return 0;
}

/* Try to lock the file noted by FP.  */
void 
lock_gnats ()
{
  char *path = (char *) xmalloc (PATH_MAX);
  struct stat buf;
  int count;

  sprintf (path, "%s/gnats-adm/gnats.lock", gnats_root);

#define MAXWAIT 10
#define GRANULARITY 1

  for (count = 0 ;
       count < MAXWAIT / GRANULARITY && stat (path, &buf) != -1 ;
       sleep(GRANULARITY), ++count)
    ;
  if (stat(path, &buf) != -1)
    {
      fprintf (stderr, "lock file exists %s\n", path);
      exit (1);
    }

  if (creat (path, 0) == -1)
    {
      fprintf (stderr, "can not create lock file: %s\n", path);
      exit (3);
    }
}

void 
unlock_gnats ()
{
  char *path = (char *) xmalloc (PATH_MAX);
  struct stat buf;

  sprintf (path, "%s/gnats-adm/gnats.lock", gnats_root);
  
  if (stat (path, &buf) < 0)
    return;

  if (unlink (path) < 0)
    {
      fprintf (stderr, "%s: cannot unlink lock file %s: %s\n",
	       program_name, path, (char *) strerror (errno));
      exit (3);
    }

}

int
gnats_locked ()
{
  char *path = (char *) xmalloc (PATH_MAX);
  struct stat buf;

  sprintf (path, "%s/gnats-adm/gnats.lock", gnats_root);
  
  if (stat (path, &buf) < 0)
    return 0;

  return 1;
}

/* VARARGS */
void 
#if defined(__STDC__) || defined(_AIX)
punt (int die, ...)
#else
punt (die, va_alist)
     int die;
     va_dcl
#endif
{
  va_list args;
  char *fmt;
  FILE *fp;

  VA_START (args, die);

  fmt = va_arg (args, char *);
  
  fp = open_mail_file (gnats_admin);
  if (fp == (FILE *) NULL)
    {
      fprintf (stderr, "can not open mail file for errors...panic!");
      unlock_gnats ();
      exit (3);
    }

  fprintf (fp, "From: gnats (GNATS Management)\n");
#ifndef USE_BIN_MAIL
  fprintf (fp, "To: %s\n", gnats_admin);
#endif
  fprintf (fp, "Subject: problem with %s\n", program_name);
  fprintf (fp, "\n\n");		/* Two newlines to massage buggy sendmails */

  vfprintf (fp, fmt, args);

  close_mail_file (fp);

  va_end (args);

  if (die)
    {
      unlock_gnats ();
      exit (2);
    }
}

void
unlock_quit ()
{
  unlock_gnats ();
  unblock_signals ();
  exit (1);
}

void
block_signals ()
{
  signal (SIGSEGV, unlock_quit);
  signal (SIGHUP, unlock_quit);
  signal (SIGTERM, unlock_quit);
  signal (SIGINT, unlock_quit);
  signal (SIGQUIT, unlock_quit);
#ifdef SIGIOT
  signal (SIGIOT, unlock_quit);
#endif
#ifdef SIGILL
  signal (SIGILL, unlock_quit);
#endif
#ifdef SIGABRT
  signal (SIGABRT, unlock_quit);
#endif
}

void
unblock_signals ()
{
  signal (SIGSEGV, SIG_DFL);
  signal (SIGHUP, SIG_DFL);
  signal (SIGTERM, SIG_DFL);
  signal (SIGINT, SIG_DFL);
  signal (SIGQUIT, SIG_DFL);
#ifdef SIGIOT
  signal (SIGIOT, SIG_DFL);
#endif
#ifdef SIGILL
  signal (SIGILL, SIG_DFL);
#endif
#ifdef SIGABRT
  signal (SIGABRT, SIG_DFL);
#endif
}
