/*
    TraceDaemon.c : Tracing daemon for the Linux kernel.
    Copyright (C) 1999, 2000 Karim Yaghmour.

    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
 */

/* Necessary headers */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <dirent.h>
#include <stdint.h>

#include <errno.h>

/* Local definitions */
#include "TraceDaemon.h"

/* Tracer properties */
#define TRACE_BUF_LEN           50000
#define TRACE_DATA_AREA_SIZE  1000000

 /* Global variables */
int      gPID;               /* The daemon's PID */
int      gBusy;              /* Are we currently writting events to the output file */
int      gTracDev;           /* File descriptor to tracing driver */
int      gTracDataFile;      /* The file to which tracing data is dumped */
int      gTracDataAreaSize;  /* The size of the are used to buffer driver data */
char*    gTracDataAreaP;     /* The primary data area */
char*    gTracDataAreaS;     /* The secondary data area */
options* gOptions;           /* The user given options */

/* Event strings the user can specify at the command line */
char *EventOT[TRACE_MAX + 1] =
{
"START",
"SYS_ENTRY",
"SYS_EXIT",
"TRAP_ENTRY",
"TRAP_EXIT",
"IRQ_ENTRY",
"IRQ_EXIT",
"SCHED",
"KTIMER",
"SIRQ",
"PROCESS",
"FS",
"TIMER",
"MEM",
"SOCKET",
"IPC",
"NET",
"",   /* Buffer start */
"",   /* Buffer end */
"",   /* New event */
"CSTM", /* Custom event */
#if SUPP_RTAI
"RTMNT",
"RTUMNT",
"RTGIRQE",
"RTGIRQX",
"RTOIRQE",
"RTOIRQX",
"RTTRAPE",
"RTTRAPX",
"RTSRQE",
"RTSRQX",
"RTSWTCHL",
"RTSWTCHR",
"RTSCHED",
"RTTASK",
"RTTIMER",
"RTSEM",
"RTMSG",
"RTRPC",
"RTMBX",
"RTFIFO",
"RTSHM",
"RTPOSIX",
"RTLXRT",
"RTLXRTI",
#endif /* SUPP_RTAI */
};

/******************************************************************
 * Function :
 *    CreateOptions()
 * Description :
 *    Creates and initializes an options structure.
 * Parameters :
 *    None.
 * Return values :
 *    The new options structure.
 * History :
 *    K.Y. 21/10/99 : Initial typing.
 * Note :
 ******************************************************************/
options* CreateOptions(void)
{
  options* pOptions;    /* Options to be created */

  /* Allocate space for new options */
  if((pOptions = (options*) malloc(sizeof(options))) == NULL)
    {
    printf("Unable to allocate space for options \n");
    exit(1);
    }

  /* Initialize options */
  pOptions->ConfigDefault      = FALSE;
  pOptions->SpecifyEvents      = FALSE;
  pOptions->EventMask          = 0;
  pOptions->SpecifyDetails     = FALSE;
  pOptions->DetailsMask        = 0;
  pOptions->ConfigCPUID        = FALSE;
  pOptions->ConfigPID          = FALSE;
  pOptions->PID                = 0;
  pOptions->ConfigPGRP         = FALSE;
  pOptions->PGRP               = 0;
  pOptions->ConfigGID          = FALSE;
  pOptions->GID                = 0;
  pOptions->ConfigUID          = FALSE;
  pOptions->UID                = 0;
  pOptions->ConfigSyscallDepth = TRUE;
  pOptions->SyscallDepth       = 0;
  pOptions->ConfigSyscallBound = FALSE;
  pOptions->UpperBound         = 0;
  pOptions->LowerBound         = 0;
  pOptions->ConfigTime         = FALSE;
  pOptions->Time.tv_sec        = 0;
  pOptions->Time.tv_usec       = 0;
  pOptions->DeviceFileName     = NULL;
  pOptions->TraceFileName      = NULL;
  pOptions->ProcFileName       = NULL;

  /* Give the options to the caller */
  return pOptions;
}

/*******************************************************************
 * Function :
 *    ParseOptions()
 * Description :
 *    Parses the command line arguments and sets the options
 *    structure to the right values.
 * Parameters :
 *    pmArgc, The number of command line options given to the program
 *    pmArgv, The argument vector given to the program
 *    pmOptions, The options structure to be set
 * Return values :
 *    NONE
 * History :
 *    K. Yaghmour, 21/10/99, Initial typing.
 * Note :
 *    This function will "exit()" if something is wrong with the
 *    command line options. If this is to go in graphic mode then
 *    gtk_init() will be called.
 *******************************************************************/
void ParseOptions(int pmArgc, char** pmArgv, options* pmOptions)
{
  int     i, j;        /* Generic indexes */
  int     lOptLen = 0; /* Length of option string */

  /* Read the command line arguments */
  for(i = 1; i < pmArgc; i++)
    {
    /* Get the option's length */
    lOptLen = strlen(pmArgv[i]);

#if 0
    /* DEBUG */
    printf("Analyzing option : %s \n", pmArgv[i]);
#endif

    /* Is this an option or a file name */
    if(pmArgv[i][0] == '-')
      {
      /* Is this long enough to really be an option */
      if(lOptLen < 2)
	{
        /* Hey Larry, Please give us something meaningfull! */
	printf("Incomplete option : %d \n", i);
	exit(1);
	}

      /* Depending on the option given */
      switch(pmArgv[i][1])
	{
	/* Default */
	case 'd':
	  pmOptions->ConfigDefault = TRUE;
	  break;

	/* Daemon buffer size */
	case 'b':
	case 'B':
	  /* Is the option given long enough */
	  if(lOptLen < 3)
	    {
	    printf("Daemon buffer size option without buffer size : %d \n", i);
	    exit(1);
	    }

	  /* Read the daemon's buffer size */
	  gTracDataAreaSize = strtol(&(pmArgv[i][2]), (char**) NULL, 10);
	  break;

	/* Specify events to be monitored */
	case 'e':
	case 'E':
	  /* Is the option given long enough */
	  if(lOptLen < 3)
	    {
	    printf("Specifying an event to monitor but no event given : %d \n", i);
	    exit(1);
	    }

	  /* We are specifying specific event */
	  pmOptions->SpecifyEvents = TRUE;

	  /* Set event to be traced */
	  for(j = 0; j <= TRACE_MAX; j++)
	    {
            /* Is this the option he gave us */
	    if(!strcmp(EventOT[j], &(pmArgv[i][2])))
	      {
	      ltt_set_bit(j, &(pmOptions->EventMask));
	      break;
	      }
	    }

	  /* Did he specify an unknown option */
	  if(j > TRACE_MAX)
	    {
	    printf("Option -e or -E accompanied by unknown event %s \n", &(pmArgv[i][2]));
	    exit(1);
	    }
	  break;

	/* Specify the events who's details are to be logged */
	case 'D' :
	  /* Is the option given long enough */
	  if(lOptLen < 3)
	    {
	    printf("Specifying an event's details to monitor but no event given : %d \n", i);
	    exit(1);
	    }

	  /* We are specifying specific event */
	  pmOptions->SpecifyDetails = TRUE;

	  /* Set event to be traced */
	  for(j = 0; j <= TRACE_MAX; j++)
	    {
            /* Is this the option he gave us */
	    if(!strcmp(EventOT[j], &(pmArgv[i][2])))
	      {
	      ltt_set_bit(j, &(pmOptions->DetailsMask));
	      break;
	      }
	    }

	  /* Did he specify an unknown option */
	  if(j > TRACE_MAX)
	    {
	    printf("Option -D accompanied by unknown event %s \n", &(pmArgv[i][2]));
	    exit(1);
	    }
	  break;

	/* Trace the CPUID */
	case 'c':
	case 'C':
	  /* Set the required information */
	  pmOptions->ConfigCPUID = TRUE;
	  break;

	/* Trace only one PID */
	case 'p':
	case 'P':
	  /* Is there enough to specify a PID */
	  if(lOptLen < 3)
	    {
	    printf("Option -p or -P needs a PID \n");
	    exit(1);
	    }

	  /* Set the required information */
	  pmOptions->ConfigPID = TRUE;
	  pmOptions->PID = strtol(&(pmArgv[i][2]), (char**) NULL, 10);
	  break;

	/* Trace only one process group */
	case 'G':
	  /* Is there enough to specify a process group */
	  if(lOptLen < 3)
	    {
	    printf("Option -G needs a process group \n");
	    exit(1);
	    }

	  /* Set the required information */
	  pmOptions->ConfigPGRP = TRUE;
	  pmOptions->PGRP = strtol(&(pmArgv[i][2]), (char**) NULL, 10);
	  break;

	/* Trace only one GID */
	case 'g':
	  /* Is there enough to specify a GID */
	  if(lOptLen < 3)
	    {
	    printf("Option -g needs a GID \n");
	    exit(1);
	    }

	  /* Set the required information */
	  pmOptions->ConfigGID = TRUE;
	  pmOptions->GID = strtol(&(pmArgv[i][2]), (char**) NULL, 10);
	  break;

	/* Trace only one UID */
	case 'u':
	case 'U':
	  /* Is there enough to specify a UID */
	  if(lOptLen < 3)
	    {
	    printf("Option -u or -U needs a UID \n");
	    exit(1);
	    }

	  /* Set the required information */
	  pmOptions->ConfigUID = TRUE;
	  pmOptions->UID = strtol(&(pmArgv[i][2]), (char**) NULL, 10);
	  break;

	/* Syscall properties */
	case 's':
	case 'S':
	  /* Is the option long enough to tell us more about the syscall setting */
	  if(lOptLen < 3)
	    {
	    printf("Option %d doesn't specify what syscall property to set \n", i);
	    exit(1);
	    }

	  /* Depending on the syscall properties */
	  switch(pmArgv[i][2])
	    {
	    /* Use depth */
	    case 'd':
	    case 'D':
	      /* Is the option long enough to tell us more about the syscall setting */
	      if(lOptLen < 4)
		{
		printf("Option %d doesn't specify the depth on which to fetch the EIP for a syscall \n", i);
		exit(1);
		}

	      /* We are fetching the eip by depth */
	      pmOptions->ConfigSyscallDepth = TRUE;
	      pmOptions->ConfigSyscallBound = FALSE;

	      /* Read the depth */
	      pmOptions->SyscallDepth = strtol(&(pmArgv[i][3]), (char**) NULL, 10);
	      break;

	    /* Specify lower limit */
	    case 'l':
	    case 'L':
	      /* Is the option long enough to tell us more about the syscall setting */
	      if(lOptLen < 4)
		{
		printf("Option %d doesn't specify the lower bound for fetch of the EIP for a syscall \n", i);
		exit(1);
		}

	      /* Was the upper bound set */
	      if(pmOptions->UpperBound == 0)
		{
		printf("Must specify upper bound before lower bound or upper bound is 0 \n");
		exit(1);
		}

	      /* We are using syscall bounds */
	      pmOptions->ConfigSyscallBound = TRUE;
	      pmOptions->ConfigSyscallDepth = FALSE;

	      /* Read the upper bound (read as 0xffffffff) */
	      pmOptions->LowerBound = strtoul(&(pmArgv[i][3]), (char**) NULL, 16);
	      break;

	    /* Specify upper limit */
	    case 'u':
	    case 'U':
	      /* Is the option long enough to tell us more about the syscall setting */
	      if(lOptLen < 4)
		{
		printf("Option %d doesn't specify the upper bound for fetch of the EIP for a syscall \n", i);
		exit(1);
		}

	      /* Read the upper bound (read as 0xffffffff) */
	      pmOptions->UpperBound = strtoul(&(pmArgv[i][3]), (char**) NULL, 16);
	      break;
	    
	    default:
	      /* I would really like to do this for you, but I wasn't taught how to! */
	      printf("Unknow option : -%c%c \n", pmArgv[i][1], pmArgv[i][2]);
	      exit(1);
	    }
	  break;

	/* Specify the time for which the daemon should run */
	case 't':
	case 'T':
	  /* Is the option long enough to tell us more about the time setting */
	  if(lOptLen < 3)
	    {
	    printf("Option %d doesn't specify what time property to set \n", i);
	    exit(1);
	    }

	  /* Depending on the time properties */
	  switch(pmArgv[i][2])
	    {
	    /* Set seconds */
	    case 's':
	    case 'S':
	      /* Is the option long enough to tell us more about the time setting */
	      if(lOptLen < 4)
		{
		printf("Option %d doesn't specify the number of seconds to run \n", i);
		exit(1);
		}

	      /* We are runing for a limited time only (like all good rebates ....) */
	      pmOptions->ConfigTime = TRUE;

	      /* Read the number of seconds */
	      pmOptions->Time.tv_sec = strtol(&(pmArgv[i][3]), (char**) NULL, 10);
	      break;

	    /* Set microseconds */
	    case 'u':
	    case 'U':
	      /* Is the option long enough to tell us more about the time setting */
	      if(lOptLen < 4)
		{
		printf("Option %d doesn't specify the number of micro-seconds to run \n", i);
		exit(1);
		}

	      /* We are runing for a limited time only (like all good rebates ....) */
	      pmOptions->ConfigTime = TRUE;

	      /* Read the number of microseconds */
	      pmOptions->Time.tv_usec = strtol(&(pmArgv[i][3]), (char**) NULL, 10);
	      break;
	    
	    default:
	      /* I would really like to do this for you, but I wasn't taught how to! */
	      printf("Unknow option : -%c%c \n", pmArgv[i][1], pmArgv[i][2]);
	      exit(1);
	    }
	  break;

	default :
	  /* I would really like to do this for you, but I wasn't taught how to! */
	  printf("Unknow option : -%c \n", pmArgv[i][1]);
	  exit(1);
	}
      }
    else /* Definitely a file name */
      {
      /* Do we already have the device file name */
      if(pmOptions->DeviceFileName == NULL)
	{
	/* Copy the file name */
	pmOptions->DeviceFileName = (char*) malloc(lOptLen + 1);
	strcpy(pmOptions->DeviceFileName, pmArgv[i]);
	}
      else
	{
	/* Do we already have trace file name */
	if(pmOptions->TraceFileName == NULL)
	  {
	  /* Copy the file name */
	  pmOptions->TraceFileName = (char*) malloc(lOptLen + 1);
	  strcpy(pmOptions->TraceFileName, pmArgv[i]);
	  }
	else
	  {
	  /* Do we already have the /proc file name */
	  if(pmOptions->ProcFileName == NULL)
	    {
	    /* Copy the file name */
	    pmOptions->ProcFileName = (char*) malloc(lOptLen + 1);
	    strcpy(pmOptions->ProcFileName, pmArgv[i]);
	    }
	  else
	    {
	    /* This guy doesn't know what he's doing! Tell him about it and get outa here ... */
	    printf("Wrong number of command line arguments \n");
	    printf("Unknown : %s \n", pmArgv[i]);
	    exit(1);
	    }
	  }
	}
      }
    }

  /* Do we have the correct arguments */
  if((pmOptions->DeviceFileName == NULL)
   ||(pmOptions->TraceFileName == NULL)
   ||(pmOptions->ProcFileName  == NULL))
    {
    /* Insufficient number of arguments */
    printf("Insufficient number of command line arguments \n");
    printf("Usage: TraceDaemon [[Options]] [TraceDevice] [TraceDumpFile] [ProcInfoFile] \n");
    exit(1);
    }
}

/******************************************************************
 * Function :
 *    ApplyConfig()
 * Description :
 *    Applies the configuration to the trace device and the daemon.
 * Parameters :
 *    pmFileID, the file ID of the device to operate on.
 *    pmOptions, the given options.
 * Return values :
 *    None.
 * History :
 *    K.Y. 21/10/99 : Initial typing.
 * Note :
 *    This function will exit if something goes wrong during the
 *    configuration.
 ******************************************************************/
void ApplyConfig(int pmFileID, options* pmOptions)
{
  struct itimerval      lTimer;     /* Timer used for daemon's execution */

  /* Do we set the tracing module to it's default configuration */
  if(pmOptions->ConfigDefault == TRUE)
    {
    /* Set the tracing module to it's default configuration */
    if(ioctl(pmFileID, TRACER_CONFIG_DEFAULT, 0))
      {
      /* Don't go any further */
      printf("TraceDaemon: Unable to set tracing module to it's default setting \n");
      exit(1);
      }
    printf("TraceDaemon: Tracing module set to default config \n");
    }

  /* Set the memory area configuration */
  if(ioctl(pmFileID, TRACER_CONFIG_MEMORY_BUFFERS, gTracDataAreaSize))
    {
    /* Don't go any further */
    printf("TraceDaemon: Unable to set data regions used for tracing \n");
    exit(1);
    }

  /* Tell the user the size of the buffers */
  printf("TraceDaemon: Trace buffers are %d bytes \n", gTracDataAreaSize);

  /* Do we set the event mask */
  if(pmOptions->SpecifyEvents == TRUE)
    {
    /* Set the event mask */
    if(ioctl(pmFileID, TRACER_CONFIG_EVENTS, &(pmOptions->EventMask)))
      {

      /* Don't go any further */
      printf("TraceDaemon: Unable to set event mask \n");
      exit(1);
      }
    printf("TraceDaemon: Set event mask 0x%016llX \n", pmOptions->EventMask);
    }

  /* Do we set the details mask */
  if(pmOptions->SpecifyDetails == TRUE)
    {
    /* Set the details mask */
    if(ioctl(pmFileID, TRACER_CONFIG_DETAILS, &(pmOptions->DetailsMask)))
      {
      /* Don't go any further */
      printf("TraceDaemon: Unable to set details mask \n");
      exit(1);
      }
    printf("TraceDaemon: Set event details mask 0x%016llX \n", pmOptions->DetailsMask);
    }

  /* Do we set to log CPUID */
  if(pmOptions->ConfigCPUID == TRUE)
    {
    /* Set to log CPUID */
    if(ioctl(pmFileID, TRACER_CONFIG_CPUID, 0))
      {
      /* Don't go any further */
      printf("TraceDaemon: Unable to set the tracer to record CPUID \n");
      exit(1);
      }
    printf("TraceDaemon: Tracking CPUID \n");
    }

  /* Do we set to track a given PID */
  if(pmOptions->ConfigPID == TRUE)
    {
    /* Set to log PID */
    if(ioctl(pmFileID, TRACER_CONFIG_PID, pmOptions->PID))
      {
      /* Don't go any further */
      printf("TraceDaemon: Unable to set the tracer to track the given PID \n");
      exit(1);
      }
    printf("TraceDaemon: Tracking PID : %d \n", pmOptions->PID);
    }

  /* Do we set to track a given process group */
  if(pmOptions->ConfigPGRP == TRUE)
    {
    /* Set to log process group */
    if(ioctl(pmFileID, TRACER_CONFIG_PGRP, pmOptions->PGRP))
      {
      /* Don't go any further */
      printf("TraceDaemon: Unable to set the tracer to track the given process group \n");
      exit(1);
      }
    printf("TraceDaemon: Tracking process group : %d \n", pmOptions->PGRP);
    }

  /* Do we set to track a given GID */
  if(pmOptions->ConfigGID == TRUE)
    {
    /* Set to log GID */
    if(ioctl(pmFileID, TRACER_CONFIG_GID, pmOptions->GID))
      {
      /* Don't go any further */
      printf("TraceDaemon: Unable to set the tracer to track the given GID \n");
      exit(1);
      }
    printf("TraceDaemon: Tracking GID : %d \n", pmOptions->GID);
    }

  /* Do we set to track a given UID */
  if(pmOptions->ConfigUID == TRUE)
    {
    /* Set to log UID */
    if(ioctl(pmFileID, TRACER_CONFIG_UID, pmOptions->UID))
      {
      /* Don't go any further */
      printf("TraceDaemon: Unable to set the tracer to track the given UID \n");
      exit(1);
      }
    printf("TraceDaemon: Tracking UID : %d \n", pmOptions->UID);
    }

  /* Do we set the syscall eip depth fetch */
  if(pmOptions->ConfigSyscallDepth == TRUE)
    {
    /* Set to fetch eip depth */
    if(ioctl(pmFileID, TRACER_CONFIG_SYSCALL_EIP_DEPTH, pmOptions->SyscallDepth))
      {
      /* Don't go any further */
      printf("TraceDaemon: Unable to set syscall fetch depth \n");
      exit(1);
      }
    printf("TraceDaemon: Fetching eip for syscall on depth : %d \n", pmOptions->SyscallDepth);
    }

  /* Do we set the syscall eip bounds */
  if(pmOptions->ConfigSyscallBound == TRUE)
    {
    /* Set to eip according to bounds */
    if(ioctl(pmFileID, TRACER_CONFIG_SYSCALL_EIP_LOWER, pmOptions->LowerBound))
      {
      /* Don't go any further */
      printf("TraceDaemon: Unable to set syscall fetch lower bound \n");
      exit(1);
      }
    /* Set to eip according to bounds */
    if(ioctl(pmFileID, TRACER_CONFIG_SYSCALL_EIP_UPPER, pmOptions->UpperBound))
      {
      /* Don't go any further */
      printf("TraceDaemon: Unable to set syscall fetch upper bound \n");
      exit(1);
      }
    printf("TraceDaemon: Fetching eip for syscall between 0x%X and 0x%X \n",
	   pmOptions->LowerBound,
	   pmOptions->UpperBound);
    }

  /* Do we run the daemon only for a given time */
  if(pmOptions->ConfigTime == TRUE)
    {
    /* Set timer properties */
    memset(&(lTimer.it_interval), 0, sizeof(lTimer.it_interval));
    memcpy(&(lTimer.it_value), &(pmOptions->Time), sizeof(lTimer.it_value));

    /* Set an alarm to wake us up when we're done */
    if(setitimer(ITIMER_REAL, &lTimer, NULL) < 0)
      {
      /* Don't go any further */
      printf("TraceDaemon: Unable to set timer to regulate daemon's trace time \n");
      exit(1);
      }
    printf("TraceDaemon: Daemon wil run for : (%ld, %ld) \n", pmOptions->Time.tv_sec, pmOptions->Time.tv_usec);
    }
}

/*******************************************************************
 * Function :
 *    SigHandler()
 * Description :
 *    The signal handling routine.
 * Parameters :
 *    pmSignalID, The ID of the signal that ocurred
 * Return values :
 *    NONE
 * History :
 *    K. Yaghmour, 05/99, Initial typing.
 * Note :
 *    This function will "exit()" if something is wrong with the
 *    command line options.
 *******************************************************************/
void SigHandler(int pmSignalID)
{
  int         lEventsLost;             /* Number of events lost */
  char*       lTempDataArea;           /* Temp pointer to data area */

  /* Depending on the received signal */
  switch(pmSignalID)
    {
    case SIGIO :
      /* Are we currently busy */
      if(gBusy == TRUE)
	{
	fprintf(stdout, "TraceDaemon: Received SIGIO while busy ... \n");
	goto end_of_trace;
	}
    
      /* We are busy */
      gBusy = TRUE;
    
      /* Exchange buffers */
      lTempDataArea  = gTracDataAreaP;
      gTracDataAreaP = gTracDataAreaS;
      gTracDataAreaS = lTempDataArea;
	  
      /* Write the filled data area to file */
      if(write(gTracDataFile, gTracDataAreaS, gTracDataAreaSize) < 0)
	{
	fprintf(stdout, "TraceDaemon: Unable to read from data space \n");
	exit(1);
	}

      /* We arent busy anymore */
      gBusy = FALSE;

      /* Tell the driver that we comitted the data */
      ioctl(gTracDev, TRACER_DATA_COMITTED, 0);

      /* We're done */
      return;
      break;

    default :
      fprintf(stdout, "TraceDaemon: Received unknown signal ... stopping \n");
    case SIGTERM :
    case SIGALRM :
      /* Stop the driver */
      ioctl(gTracDev, TRACER_STOP, 0);

      /* Read the number of events lost */
      lEventsLost = ioctl(gTracDev, TRACER_GET_EVENTS_LOST, 0);

      /* Where there any events lost */
      if(lEventsLost > 0)
	{
	printf("TraceDaemon: WARNING!!!! \n");
	printf("TraceDaemon: %d EVENTS HAVE BEEN LOST \n", lEventsLost);
	printf("TraceDaemon: Check the size of the buffers \n");
	printf("TraceDaemon: Current buffer size %d \n", gTracDataAreaSize);
	}
    }

end_of_trace:
  /* Write what's in the current data area to file */
  write(gTracDataFile, gTracDataAreaP, gTracDataAreaSize);

  /* OK we're out of here */
  close(gTracDev);
  close(gTracDataFile);
      
  /* Tell the user that we're done */
  fprintf(stdout, "\nTraceDaemon: End of tracing \n");
  _exit(0);
}

/*******************************************************************
 * Function :
 *    MapProc()
 * Description :
 *    Function maping out /proc.
 * Parameters :
 *    pmProcFile, file where the information is to be written
 * Return values :
 *    NONE
 * History :
 *    K. Yaghmour, 05/99, Initial typing.
 *******************************************************************/
void MapProc(int pmProcFile)
{
  int             i;              /* Generic index */
  int             lIRQ;           /* IRQ ID */
  int             lPID;           /* Process' PID */
  int             lPPID;          /* Parent's PID */
  int             lNbCPU;         /* Number of CPUs */
  int             lSizeRead;      /* Quantity of data read from file */
  int             lStatusFile;    /* File containd process status */
  char            lFileName[40];  /* A string containing a complete file name */
  char            lDataBuf[500];  /* Status file content */
  char            lName[256];     /* Name of the process */
  char*           lString;        /* Generic string */
  DIR*            pDir;           /* Directory pointer */
  FILE*           pIntFile;       /* Int pile pointer */
  struct dirent*  pDirEntry;      /* Directory entry */

  /* Open /proc */
  if((pDir = opendir("/proc")) == NULL)
    {
    printf("\nYour system doesn't support /proc \n");
    exit(1);
    }

  /* Read the directory through */
  while((pDirEntry = readdir(pDir)) != NULL)
    {
    /* Is this a directory entry describing a PID */
    if(isdigit(pDirEntry->d_name[0]))
      {
      /* Get the PID in number form */
      lPID = atoi(pDirEntry->d_name);

      /* Open the status file */
      sprintf(lFileName, "/proc/%d/status", lPID);
      if((lStatusFile = open(lFileName, O_RDONLY, 0)) < 0)
	continue;

      /* Read a little bit */
      if((lSizeRead = read(lStatusFile, lDataBuf, 500)) == 0)
	continue;

      /* Close the file */
      close(lStatusFile);
      
      /* Find Name and Parent PID */
      lString  = strstr(lDataBuf, "Name");
      sscanf(lString, "Name: %s", lName);
      lString  = strstr(lDataBuf, "PPid");
      sscanf(lString, "PPid: %d", &lPPID);

      /* Print out the precious information */
      sprintf(lDataBuf, "PID: %d; PPID: %d; NAME: %s\n", lPID, lPPID, lName);
      write(pmProcFile, lDataBuf, strlen(lDataBuf));
      }
    }

  /* Close the directory */
  closedir(pDir);

  /* Open the interrupts information file */
  if((pIntFile = fopen("/proc/interrupts", "r")) == NULL)
    {
    printf("open /proc/interrupts failed");
    return;
    }
  
  /* The first line is the column header, usually just CPU0, CPU1, etc.
     Count the number of CPUs based on "CPUn" occurences.  I didn't
     see anything else in /proc that would have been any easier
     and I need to be in this pseudofile anyway... */
  if(fgets(lDataBuf, 200, pIntFile) <= 0)
    { /* Error or EOF */
    printf("read /proc/interrupts failed");
    fclose(pIntFile);
    return;
    }

  /* Initialize the number of CPUs */
  lNbCPU = 0;

  /* Print header for CPU 0 */
  sprintf(lName, "CPU%d", lNbCPU);

  /* Find entry for CPU 0 */
  lString = strstr(lDataBuf, lName);

  /* Go through rest of string */
  while (lString)
    {
    /* Look for next CPU info */
    lNbCPU++;
    sprintf(lName, "CPU%d", lNbCPU);
    lString = strstr(lString, lName);
    }

  /* How many CPUs did we find */
  if (lNbCPU <= 0 || lNbCPU > 128)
    {  
    /* Missing or too weird */
    printf("Can't determine CPU count /proc/interrupts\n");
    fclose(pIntFile);
    return;
    }

  /* At the beginning of each valid IRQ line is a digit.  Scan for 
     it, skip through the CPU hit fields, skip the IRQ supplier (or 
     whatever that field is) and grab the rest.  This assumes the 
     IRQ supplier has no white space in the middle.  Outer fscanf
     stops on EOF, "NMI", "ERR", or other non-numeric chars. */

  /* Go to the next interrupt description in the file */
  while(fscanf(pIntFile, "%d:", &lIRQ) == 1)
    {
    /* Is this a valid  */
    if(lIRQ > 256)
      {
      printf("IRQ %d is out of range in /proc/interrutps\n", lIRQ);
      fclose(pIntFile);
      return;
      }

    /* For all CPUs */
    for(i = 0; i < lNbCPU; i++)
      /* Read one int per CPU */
      if(fscanf(pIntFile, "%s", lDataBuf) != 1)
	{
	printf("Bad INT count field seen in /proc/interrutps\n");
	fclose(pIntFile);
	return;
	}
	    
    /* The trailing space is crucial in the next format because
       it gets rid of all whitespace after the supplier field.
       That sets it up for the final fgets() */
     
    /* Read a whitespace */
    if(fscanf(pIntFile, "%s ", lDataBuf) != 1)
      {
      printf("Bad INT source field seen in /proc/interrutps\n");
      fclose(pIntFile);
      return;
      }

    /* Get the rest of the line */
    fgets(lName, 100, pIntFile);

    /* Format string describing the IRQ */
    sprintf(lDataBuf, "IRQ: %d; NAME: %s", lIRQ, lName);

    /* Write string to proc file */
    write(pmProcFile, lDataBuf, strlen(lDataBuf));
    }

  /* Close the interrupt information file */
  fclose(pIntFile);
}

/*******************************************************************
 * Function :
 *    main()
 * Description :
 *    The daemon entry point.
 * Parameters :
 *    pmArgc, the number of command line arguments.
 *    pmArgv, the argument vector.
 * Return values :
 *    NONE (even if declared as int).
 * History :
 *    K. Yaghmour, 05/99, Initial typing.
 *******************************************************************/
int main(int pmArgc, char** pmArgv)
{
  int              lPID;       /* The pid of the daemon */
  int              lProcFile;  /* File to which current process information is dumped */
  struct sigaction lSigAct;    /* Structure passed on to sigaction() */

  /* Initialize the busy flag */
  gBusy = FALSE;

  /* Initialize data area size */
  gTracDataAreaSize = TRACE_DATA_AREA_SIZE;

  /* Allocate space for the options */
  gOptions = CreateOptions();

  /* Parse the given options */
  ParseOptions(pmArgc, pmArgv, gOptions);

  /* Become daemon */
  if((lPID = fork()) < 0)
    {
    /* Print an error message */
    printf("\nUnable to become daemon \n");

    /* Exit */
    exit(1);
    }
  else if(lPID)
    /* The parent dies */
    exit(0);

  /* Become session leader */
  gPID = setsid();

  /* Initialize structure passed to sigaction() */
  lSigAct.sa_handler = SigHandler;          /* Signal hanlder */
  sigfillset(&(lSigAct.sa_mask));           /* Signal mask */
  lSigAct.sa_flags = 0;                     /* Action flags (No re-register or unblock) */

  /* Set up the signal handler */
  if(sigaction(SIGIO, &lSigAct, NULL)) exit(1);
  if(sigaction(SIGTERM, &lSigAct, NULL)) exit(1);
  if(sigaction(SIGALRM, &lSigAct, NULL)) exit(1);

  /* Open the file to which the tracing data should be written */
  if((gTracDataFile = open(gOptions->TraceFileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0)
    {
    /* File ain't good */
    printf("\nUnable to open output file (errno = %d) \n", errno);
    exit(1);
    }  

  /* The output file is ready */
  printf("TraceDaemon: Output file ready \n");

  /* Open the tracing driver */
  if((gTracDev = open(gOptions->DeviceFileName, O_RDONLY, 0)) < 0)
    {
    /* Device ain't good */
    printf("\nTraceDaemon: Unable to open specified device : %s \n", gOptions->DeviceFileName);
    close(gTracDataFile);
    exit(1);
    }

  /* We opened the driver */
  printf("TraceDaemon: Trace driver open \n");

  /* Apply the configuration */
  ApplyConfig(gTracDev, gOptions);

  /* Map the driver's buffers in our address space */
  gTracDataAreaP = mmap(NULL,
			gTracDataAreaSize << 1,
			PROT_READ | PROT_WRITE,
			MAP_PRIVATE,
			gTracDev,
			0);

  /* Did the mapping work out */
  if(gTracDataAreaP == MAP_FAILED)
    {
    printf("TraceDaemon: Unable to map driver's data area in daemon's space \n");
    close(gTracDataFile);
    close(gTracDev);
    exit(1);
    }

  /* Set the secondary data area */
  gTracDataAreaS = gTracDataAreaP + gTracDataAreaSize;

#if 0
  printf("TraceDaemon: Primary data area is at   => 0x%08X \n", gTracDataAreaP);
  printf("TraceDaemon: Secondary data area is at => 0x%08X \n", gTracDataAreaS);
#endif

#if 0
  gTracDataAreaP[0] = 0xF;
#endif

  /* Fire up the tracing */
  if(ioctl(gTracDev, TRACER_START, 0) < 0)
    {
    /* Don't go any further */
    printf("TraceDaemon: Unable to start the tracing \n");
    exit(1);
    }

#if 0
  if(gTracDataAreaP[0] == 0)
    {
    printf("TraceDaemon: tracing isn't functionning \n");
    exit(1);
    }
  printf("TraceDaemon: First byte is %d \n", gTracDataAreaP[0]);
#endif

  /* Map out /proc into user given file */
  if((lProcFile = creat(gOptions->ProcFileName, S_IRUSR| S_IWUSR | S_IRGRP | S_IROTH)) < 0)
    {
    /* File ain't good */
    printf("\nUnable to open output file to dump current process information \n");
    exit(1);
    }

  /* Map out /proc */
  MapProc(lProcFile);
  
  /* Tell the user that mapping /proc is done */
  printf("TraceDaemon: Done mapping /proc \n");
  
  /* Close the output file */
  close(lProcFile);

  /* Sleep forever */
  while(1)
    pause();

  /* SHOULD NEVER GET HERE! */
  exit(1);
}
