/*************************************************
*                      Smailmon                  *
*************************************************/

/* Copyright (c) University of Cambridge 1995. */

/* P. Hazel, February 1995*/
/* Last Modified: March 1995 */

#include "smailmon.h"

/* This module contains code to fork off a process to run the
privileged (mayble) readspool program, which makes it possible
to read the headers of spool files. */


static int pipe_fd = -1;
static int ptr;
static int length;
static jmp_buf jmp_env;
static char read_buffer[1024];



/*************************************************
*          Tidy incompletely read pipe           *
*************************************************/

/* We must also wait for the sub-process to go away,
and since there is only supposed to be one sub-process
running at once, wait for all of them to be on the
safe side. */

void read_spool_tidy(void)
{
if (pipe_fd >= 0) { close(pipe_fd); pipe_fd = -1; }
while (wait(NULL) >= 0);
}



/*************************************************
*             Signal handler                     *
*************************************************/

/* Handler for unexpected signals during a pipe read. */

static void handler(int signal)
{
longjmp(jmp_env, 1);
}


/*************************************************
*           Get next line from spool file        *
*************************************************/

/* The line is returned without a trailing newline. */

int read_spool(char *buffer)
{
char *p = buffer;

if (pipe_fd < 0) return FALSE;

for (;;)
  {
  while (ptr < length)
    {
    if ((*p++ = read_buffer[ptr++]) == '\n')
      {
      p[-1] = 0;
      return TRUE;
      }      
    } 
  
  ptr = 0;

  /* For reasons which are unknown, the following statement sometimes
  collapses with a bus error or a segmentation error. For the moment, 
  we paper over this crack... */
  
  signal(SIGBUS, handler);   
  signal(SIGSEGV, handler); 
  if (setjmp(jmp_env) == 0)
    length = read(pipe_fd, read_buffer, sizeof(read_buffer));
      else length = 0;   
  signal(SIGSEGV, SIG_IGN);   
  signal(SIGBUS, SIG_IGN); 
  
  if (length <= 0)
    {
    read_spool_tidy(); 
    *p = 0;
    return (p != buffer);   
    }
  }      
}



/*************************************************
*            Set up pipe and fork                *
*************************************************/

/* The argument is the spool id to be passed to readspool */

int read_spool_init(char *name)
{
char *s;
int pfd[2];

/* In case not previously tidied */

read_spool_tidy();

/* Set up the name of the readspool program from the name
of the smailmon binary. */

strcpy(read_buffer, smailmon_name);
for (s = read_buffer+(int)strlen(read_buffer)-1; s >= read_buffer; s--)
  {
  if (*s == '/') break; 
  }  
strcpy(s+1, "readspool");    

/* Create the pipes and then fork, etc. */

if (pipe(pfd) == 0)
  {
  int pid = fork();
   
  /* The child process makes the writing end of the pipe into the
  standard output, then execs to readspool, having closed all 
  other file descriptors. */
    
  if (pid == 0)
    { 
    struct rlimit lim;
    if (getrlimit(RLIMIT_NOFILE, &lim) == 0)
      { 
      int fd = lim.rlim_max; 
      for (--fd; fd >= 0; fd--) if (fd != pfd[1]) close(fd);
      if (pfd[1] != 1) 
        {
        dup2(pfd[1], 1);  /* Make the standard output */
        close(pfd[1]);
        }  
      execle(read_buffer, "readspool", name, (char *)0, NULL);
      }
    exit(0);   /* Just in case */    
    }
  
  /* Parent */

  close(pfd[1]);
  if (pid > 0)          /* fork succeeded */
    {     
    pipe_fd = pfd[0];
    ptr = length = 0;    
    return TRUE; 
    }
  else                  /* fork failed */
    {
    close(pfd[0]);
    return FALSE;  
    }      
  }
else return FALSE;   
}   


/* End of callreadspool.c */
