/*
 * tlock - time clock a function call
 * Copyright (C) January, 2000 Sergei Viznyuk <sv@phystech.com>
 * 
 * This 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <sys/types.h>
#include <sys/wait.h>
#include <setjmp.h>
//#include <stdarg.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#ifdef DEBUG
#include <stdio.h>
#endif
#define MAX_SIGNALS  32

#ifdef SunOS
sigjmp_buf tlock_env;
#else
jmp_buf	tlock_env;
#endif
pid_t	child_pid;

int creadok(int s,unsigned tmout);
int cwriteok(int s,unsigned tmout);

int rtv;
unsigned itv;
/*****************************************************************************/
void tsighandler(sig)
int sig;
{
  siglongjmp(tlock_env,sig);
}
/*****************************************************************************/
int tlock(unsigned *tml,int (*fct)(),char **ar)
{
  int sv[2],rv[2];
//  va_list ap;
  struct sigaction action,oction[MAX_SIGNALS];
  if ( tml == NULL )
    {
      errno = EINVAL;
      return -1;
    }
  if ( pipe(sv) == -1 ) return -1;
  if ( pipe(rv) == -1 ) return -1;
  action.sa_handler = &tsighandler;
  action.sa_flags = 0;
  for (itv=1;itv<SIGCHLD;itv++)
    sigaction(itv,&action,&oction[itv-1]);
  for (itv=SIGCHLD+1;itv<MAX_SIGNALS;itv++)
    sigaction(itv,&action,&oction[itv-1]);
  action.sa_handler = SIG_IGN;
  sigaction(SIGCHLD,&action,&oction[SIGCHLD-1]);
  rtv= -1;
  child_pid=fork();
  switch ( child_pid )
    {
      case 0:
        close(sv[1]);
        close(rv[0]);
        if ( (itv=sigsetjmp(tlock_env,0xffff)) )
	  {
#ifdef DEBUG
	    fprintf(stderr,"child: caught signal %d\n",itv);
#endif
	    sigaction(itv,&action,NULL);
#ifdef DEBUG
	    fprintf(stderr,"child: kill(%d,%d) returned %d\n",
	    getppid(),itv,kill(getppid(),itv));
#endif
	    kill(getppid(),itv);
	    break;
	  }
        if ( *tml )
          while ( itv < *tml )
	    if ( (rtv=creadok(sv[0],1000000/CLK_TCK)) > 0 )
	      itv++;
	    else
	      break;
        else
          while ( 1 )
	    if ( (rtv=creadok(sv[0],1000000/CLK_TCK)) > 0 )
	      itv++;
	    else
	      break;
#ifdef DEBUG
        fprintf(stderr,"child: creadok returned %d, count=%d\n",rtv,itv);
#endif
	if ( rtv )
#ifdef DEBUG
	  fprintf(stderr,"child: kill(%d,%d) returned %d\n",
	  getppid(),SIGUSR2,kill(getppid(),SIGUSR2));
#else
	  kill(getppid(),SIGUSR2);
#endif
	else
#ifdef DEBUG
	  fprintf(stderr,"child: %d bytes read\n",
	  read(sv[0],tml,sizeof(unsigned)));
#else
	  read(sv[0],tml,sizeof(unsigned));
#endif
        *tml=itv;
        if ( (itv=cwriteok(rv[1],1000000/CLK_TCK)) == 0 )
#ifdef DEBUG
	  fprintf(stderr,"child: cwriteok returned %d: %d bytes written\n",
	  itv,write(rv[1],tml,sizeof(unsigned)));
#else
	  write(rv[1],tml,sizeof(unsigned));
#endif
        break;
      case -1:
        close(sv[0]);
        close(rv[1]);
        break;
      default:
        close(sv[0]);
        close(rv[1]);
        switch ( (itv=sigsetjmp(tlock_env,0xffff)) )
	  {
	    case 0:
              *tml = 0;
//	      va_start(ap,ftc);
	      rtv=fct(ar);
#if DEBUG
	      fprintf(stderr,"parent: ftc returned %d\n",rtv);
#endif
              if ( cwriteok(sv[1],1000000/CLK_TCK) == 0 )
#ifdef DEBUG
	        fprintf(stderr,"parent: cwrite returned 0: %d bytes written\n",
			write(sv[1],tml,sizeof(unsigned)));
#else
	        write(sv[1],tml,sizeof(unsigned));
#endif
	    case SIGUSR2:
#ifdef DEBUG
	      if ( itv==SIGUSR2 )
	        fprintf(stderr,"parent: caught SIGUSR2\n");
#endif
	      if ( (itv=creadok(rv[0],1000000/CLK_TCK)) == 0 )
#ifdef DEBUG
	        fprintf(stderr,"parent: creadok returned %d: %d bytes read\n",
		      itv,read(rv[0],tml,sizeof(unsigned)));
#else
	        read(rv[0],tml,sizeof(unsigned));
#endif
	      break;
	    default:
#ifdef DEBUG
	      fprintf(stderr,"parent: caught signal %d\n",itv);
#endif
	      sigaction(itv,&action,NULL);
#ifdef DEBUG
	      fprintf(stderr,"parent: kill(%d,%d) returned %d\n",
	      child_pid,itv,kill(child_pid,itv));
#else
	      kill(child_pid,itv);
#endif
	  }
    }
  if ( child_pid )
    {
      close(sv[1]);
      close(rv[0]);
      waitpid(child_pid,NULL,0);
      for (itv=1;itv<MAX_SIGNALS;itv++) sigaction(itv,&oction[itv-1],NULL);
      return rtv;
    }
  close(sv[0]);
  close(rv[1]);
  exit(0);
}


syntax highlighted by Code2HTML, v. 0.9.1