//-----------------------------------------------------------------------------
// Copyright (c) 1994,1995 Southeastern Universities Research Association,
//                         Continuous Electron Beam Accelerator Facility
//
// This software was developed under a United States Government license
// described in the NOTICE file included as part of this distribution.
//
//-----------------------------------------------------------------------------
//
// Description:
//       cdevAccount classes:  These classes provide the basic functionality
//       for CDEV accounting. 
//
// Author:  Walt Akers
//-----------------------------------------------------------------------------

#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/utsname.h>
#include <cdevAccount.h>

// *****************************************************************************
// * Define _IGNORE_ARGV_ to prevent the application from learning the program
// * name using dubious measures.
// *****************************************************************************
#define _IGNORE_ARGV_ 1

char cdevAccountRecord::tempBuf[cdevAccountRecord::MAX_TEMP_BUF];

cdevAccountEntry *cdevGlobalAccountEntry = NULL;

// *****************************************************************************
// * This is the method that will be register with at exit in order to remove
// * the cdevGlobalAccountEntry.
// *****************************************************************************
void removeGlobalAccountRecord ( void )
	{
	if(cdevGlobalAccountEntry!=NULL)
		{
		delete cdevGlobalAccountEntry;
		cdevGlobalAccountEntry = NULL;
		}
	}

// *****************************************************************************
// * cdevAccountRecord::cdevAccountRecord :
// *	This is the constructor for the cdevAccountRecord object.  It initializes
// *	all of the internal variables.
// *****************************************************************************
cdevAccountRecord::cdevAccountRecord  ( void ) : 
	serviceCnt(0), serviceList(0), serviceMax(0), argList(0), argCnt(0), next(NULL)
	{
	reset();
	}

// *****************************************************************************
// * cdevAccountRecord::~cdevAccountRecord :
// *	This is the destructor for the cdevAccountRecord object.  it deletes any
// *	memory that may have been allocated by the class.
// *****************************************************************************
cdevAccountRecord::~cdevAccountRecord ( void )
	{
	reset();
	}

// *****************************************************************************
// * cdevAccountRecord::reset :
// *	This method restores the data objects in the class to their initial
// *	values.
// *****************************************************************************
void cdevAccountRecord::reset ( void )
	{
	int i;

	for(i=0; i<argCnt; i++)     delete argList[i];
	if (argList!=NULL)          delete argList;
	for(i=0; i<serviceCnt; i++) delete serviceList[i];
	if(serviceList!=NULL)       delete serviceList;

	processID    = 0;
	*startTime   = 0;
	*appName     = 0;
	argCnt       = 0;
	argList      = 0;
	*userName    = 0;
	*hostName    = 0;
	*osName      = 0;
	*osRelease   = 0;
	*cdevVersion = 0;
	serviceCnt   = 0;
	serviceMax   = 0;
	serviceList  = 0;
	}


// *****************************************************************************
// * cdevAccountRecord::streamIn :
// *	This method is used to read the next available accounting record
// *	from an open file.
// *****************************************************************************
int cdevAccountRecord::streamIn ( FILE * fp )
	{
	return streamIn(fileno(fp));
	}

// *****************************************************************************
// * cdevAccountRecord::streamIn :
// *	This method is used to read the next available accounting record
// *	from an open file.
// *****************************************************************************
int cdevAccountRecord::streamIn ( int fd )
	{
	char * sPtr;
	int    amntread = 0;
	int    startPos = (int)lseek(fd, 0L, SEEK_CUR);
	
	*tempBuf = 0;
	if((amntread = read(fd, tempBuf, MAX_TEMP_BUF-1))>0)
		{
		tempBuf[amntread] = 0;
		for(sPtr=tempBuf; isspace(*sPtr) && *sPtr; sPtr++);
		if((sPtr = strchr(sPtr, '\n'))!=NULL)
			{
			*sPtr = 0;
			lseek(fd, startPos+strlen(tempBuf), SEEK_SET);
			}
		streamIn(tempBuf);
		}
	return (*hostName==0 || processID==0 || *cdevVersion==0)?-1:0;
	}

// *****************************************************************************
// * cdevAccountRecord::streamIn :
// *	This method allows the caller to read the next available accounting
// *	record from a previously loaded data buffer.  This function will return
// *	a pointer to the next character following the record in the data buffer.
// *****************************************************************************
char * cdevAccountRecord::streamIn ( char * buf )
	{
	int i;
	char * ptr    = NULL;
	char * ePtr   = NULL;
	char * retval = NULL;

	reset();

	while(buf && isspace(*buf) && *buf) buf++;	
	if(buf && *buf && (ePtr = strchr(buf, '\n'))!=NULL)
		{
		*ePtr  = 0;
		retval = ePtr;
		}
	else if(buf && *buf) retval = buf+strlen(buf);
        else retval = NULL;
	
	if(buf && *buf) strcpy(hostName,    ((ptr=strtok(buf,  ";"))==NULL)?"\0":ptr);
	if(ptr) processID =         ((ptr=strtok(NULL, ";"))==NULL)?0:atoi(ptr);
	if(ptr) strcpy(startTime,   ((ptr=strtok(NULL, ";"))==NULL)?"\0":ptr);
	if(ptr) strcpy(appName,     ((ptr=strtok(NULL, ";"))==NULL)?"\0":ptr);
	if(ptr) strcpy(userName,    ((ptr=strtok(NULL, ";"))==NULL)?"\0":ptr);
	if(ptr) strcpy(osName,      ((ptr=strtok(NULL, ";"))==NULL)?"\0":ptr);
	if(ptr) strcpy(osRelease,   ((ptr=strtok(NULL, ";"))==NULL)?"\0":ptr);
	if(ptr) strcpy(cdevVersion, ((ptr=strtok(NULL, ";"))==NULL)?"\0":ptr);
	if(ptr && (argCnt =        ((ptr=strtok(NULL, ";"))==NULL)?0:atoi(ptr))>0)
		{
		argList = new char *[argCnt+1];
		for(i=0; i<argCnt && ptr; i++)
			{
			argList[i] = strdup(((ptr=strtok(NULL, ";"))==NULL)?"\0":ptr);
			}
		argList[argCnt] = 0;
		}
	if(ptr && (serviceCnt = ((ptr = strtok(NULL, ";"))==NULL)?0:atoi(ptr))>0)
		{
		serviceMax  = serviceCnt+1;
		serviceList = new char *[serviceMax+1];
		for(i=0; i<serviceCnt && ptr; i++)
			{
			serviceList[i] = strdup(((ptr=strtok(NULL, ";"))==NULL)?"\0":ptr);
			}
		serviceList[serviceCnt] = 0;
		}

	if(ePtr!=NULL) *ePtr='\n';

	return retval;
	}
	
// *****************************************************************************
// * cdevAccountRecord::streamOut :
// *	This method is used to write the next availabl accounting record
// *	to an open file.
// *****************************************************************************
int cdevAccountRecord::streamOut ( FILE * fp )
	{
	int result = streamOut(fileno(fp));
	fflush(fp);
	return result;
	}

// *****************************************************************************
// * cdevAccountRecord::streamOut :
// * 	This method is used to write the next availabl accounting record to
// *	an open file.  This function assumes that the file descriptor is
// *	in the correct place and locks are held on the file.
// *****************************************************************************
int cdevAccountRecord::streamOut ( int fd )
	{
	int    result;
	char * ptr;
	char * record;
	int    i;
	int    reclen = strlen(hostName)+
			strlen(ltoa(processID))+
	                strlen(startTime)+
	                strlen(appName)+
	                strlen(userName)+
	                strlen(osName)+
	                strlen(osRelease)+
	                strlen(cdevVersion)+
	                strlen(ltoa(argCnt))+
	                strlen(ltoa(serviceCnt));
	for(i=0; i<argCnt; i++) reclen+=strlen(argList[i])+1;
	for(i=0; i<serviceCnt; i++) reclen+=strlen(serviceList[i])+1;
	reclen+=32;
	
	if(reclen<MAX_TEMP_BUF) record = tempBuf;
	else record = new char[reclen];
			
	sprintf(record, "%s;%i;%s;%s;%s;%s;%s;%s;%i;",
			hostName,
			processID,
			startTime,
			appName, 
			userName,
			osName, 
			osRelease,
			cdevVersion,
			argCnt);
	ptr = record+strlen(record);
	for(i=0; i<argCnt; i++) ptr+=sprintf(ptr, "%s;", argList[i]);
	ptr+=sprintf(ptr, "%i;", serviceCnt);
	for(i=0; i<serviceCnt; i++) ptr+=sprintf(ptr, "%s;", serviceList[i]);
	strcpy(ptr, "\n");
	
	// *********************************************************************
	// * Write the record out to the file.
	// *********************************************************************
	result = write(fd, record, strlen(record));		
	if(record!=tempBuf) delete record;

	return result;	
	}
	
// *****************************************************************************
// * cdevAccountRecord::asciiDump :
// *	This method dumps the contents of the accounting record in an
// *	ASCII debugging format to the specified file descriptor.
// *****************************************************************************
void cdevAccountRecord::asciiDump ( FILE * fp )
	{
	int i;

	fprintf(fp, "CDEV Application Information...\n");
	fprintf(fp, "     Process ID    = %i\n", processID);
	fprintf(fp, "     Start Time    = %s\n", startTime); 
	fprintf(fp, "     Application   = %s\n", appName);
	fprintf(fp, "     Argument List = %s\n", (argList!=NULL)?argList[0]:"\0");
	for(i=1; i<argCnt; i++)
		{
		fprintf(fp, "                     %s\n", argList[i]);
		}
	fprintf(fp, "     User Name     = %s\n", userName);
	fprintf(fp, "     Host Name     = %s\n", hostName);
	fprintf(fp, "     OS Name       = %s\n", osName);
	fprintf(fp, "     OS Release    = %s\n", osRelease);
	fprintf(fp, "     CDEV Version  = %s\n", cdevVersion);
	fprintf(fp, "     CDEV Services = %s\n", serviceCnt>0?serviceList[0]:"\0");
	for(i=1; i<serviceCnt; i++)
		{
		fprintf(fp, "                     %s\n", serviceList[i]);
		}
	fprintf(fp, "\n");
	}
	
// *****************************************************************************
// * cdevAccountRecord::getNext :
// * 	Returns the next record in a list of cdevAccountRecord objects.
// *****************************************************************************
cdevAccountRecord * cdevAccountRecord::getNext ( void )
	{
	return next;
	}
	

// *****************************************************************************
// * cdevAccountRecord::setNext :
// * 	Allows the caller to set the next record in a list of cdevAccountRecord
// *	objects.
// *****************************************************************************
void cdevAccountRecord::setNext ( cdevAccountRecord * Next )
	{
	next = Next;
	}


// *****************************************************************************
// * cdevAccountRecord::getProcessID :
// * 	This method allows the caller to retrieve the process ID of the 
// *	CDEV accounting record.
// *****************************************************************************
pid_t cdevAccountRecord::getProcessID ( void ) 
	{
	return processID;
	}

// *****************************************************************************
// * cdevAccountRecord::setProcessID :
// *	This method allows the caller to set the process ID of the CDEV
// *	accounting record.
// *****************************************************************************
void cdevAccountRecord::setProcessID ( pid_t ProcessID )
	{
	processID = ProcessID;
	}

// *****************************************************************************
// * cdevAccountRecord::getStartTime :
// *	This method allows the caller to obtain a string that contains the
// *	starting time of the CDEV accounting record.
// *****************************************************************************
char * cdevAccountRecord::getStartTime ( void )
	{
	return startTime; 
	}

// *****************************************************************************
// * cdevAccountRecord::setStartTime :
// *	This method allows the caller to set the starting time of the CDEV
// *	accounting record using a time_t object.
// *****************************************************************************
void cdevAccountRecord::setStartTime ( time_t StartTime )
	{
	strftime(startTime, 20, "%m/%d/%Y %H:%M", gmtime(&StartTime));
	}

// *****************************************************************************
// * cdevAccountRecord::getAppName :
// *	This method allows the caller to get the name of the CDEV
// *	application.
// *****************************************************************************
char * cdevAccountRecord::getAppName ( void )
	{
	return appName;
	}

// *****************************************************************************
// * cdevAccountRecord::setAppName :
// *	This method allows the caller to set the name of the CDEV
// *	application.
// *****************************************************************************
void cdevAccountRecord::setAppName ( char * AppName )
	{
	if(AppName && *AppName) strcpy(appName, AppName);
	else *appName = 0;
	}

// *****************************************************************************
// * cdevAccountRecord::getArguments :
// * 	This method allows the caller to get the arguments that were used 
// *	to start the CDEV application.  The list of entries is terminated
// *	by a NULL entry.
// *****************************************************************************
char ** cdevAccountRecord::getArguments ( void )
	{
	return argList; 
	}

// *****************************************************************************
// * cdevAccountRecord::setArguments :
// *	This method allows the caller to set the arguments that were used to
// *	start the CDEV application.
// *****************************************************************************
void cdevAccountRecord::setArguments ( char ** args, int nArgs)
	{
	while(argCnt>0) delete argList[--argCnt];
	if(argList!=NULL)
		{
		delete argList;
		argList = NULL;
		}
	if(nArgs>0)
		{
		char * ptr;
		argList = new char *[nArgs+1];
		for(argCnt=0; argCnt<nArgs; argCnt++)
			{
			argList[argCnt] = strdup(args[argCnt]);
			
			// *****************************************************
			// * Remove the characters ';' and '\n' from the
			// * argument list because these characters will confuse
			// * the accounting parser.
			// *****************************************************
			for(ptr=argList[argCnt]; (ptr=strchr(ptr, ';'))!=NULL; *ptr = ' ');
			for(ptr=argList[argCnt]; (ptr=strchr(ptr, '\n'))!=NULL; *ptr = ' ');
			}
		argList[argCnt] = NULL;
		}
	}

// *****************************************************************************
// * cdevAccountRecord::getUserName :
// *	This method allows the caller to get the user name that started the
// *	CDEV application.
// *****************************************************************************
char * cdevAccountRecord::getUserName ( void )
	{
	return userName;
	}

// *****************************************************************************
// * cdevAccountRecord::setUserName :
// *	This method allows the caller to set the user name that started a
// *	CDEV application.
// *****************************************************************************
void cdevAccountRecord::setUserName ( char * UserName )
	{
	if(UserName && *UserName) strcpy(userName, UserName);
	else *userName = 0;
	}

// *****************************************************************************
// * cdevAccountRecord::getHostName :
// *	This method allows the caller to get the name of the host where the
// *	CDEV application was started.
// *****************************************************************************
char * cdevAccountRecord::getHostName ( void )
	{
	return hostName;
	}

// *****************************************************************************
// * cdevAccountRecord::setHostName :
// *	This method allows the caller to set the name of the host where the
// *	CDEV application was started.
// *****************************************************************************
void cdevAccountRecord::setHostName ( char * HostName )
	{
	if(HostName && *HostName) strcpy(hostName, HostName);
	else *hostName = 0;
	}

// *****************************************************************************
// * cdevAccountRecord::getSystemName :
// *	This method allows the caller to obtain the operating system type
// *	of the host where the CDEV appliication was started.
// *****************************************************************************
char * cdevAccountRecord::getSystemName ( void )
	{
	return osName; 
	}

// *****************************************************************************
// * cdevAccountRecord::setSystemName :
// *	This method allows the caller to set the operating system type of
// *	the host where the CDEV application was started.
// *****************************************************************************
void cdevAccountRecord::setSystemName ( char * OSName )
	{
	if(OSName && *OSName) strcpy(osName, OSName);
	else *osName = 0;
	}

// *****************************************************************************
// * cdevAccountRecord::getSystemRelease :
// *	This method allows the caller to get the release number of the 
// *	operating system where the CDEV application was started.
// *****************************************************************************
char * cdevAccountRecord::getSystemRelease ( void )
	{
	return osRelease;
	}

// *****************************************************************************
// * cdevAccountRecord::setSystemRelease :
// *	This method allows the caller to set the release number of the operating
// *	system where the CDEV application was started.
// *****************************************************************************
void cdevAccountRecord::setSystemRelease ( char * OSRelease )
	{
	if(OSRelease && *OSRelease) strcpy(osRelease, OSRelease);
	else *osRelease = 0;
	}

// *****************************************************************************
// * cdevAccountRecord::getCDEVVersion :
// *	This method allows the caller to retrieve the CDEV version number of 
// *	the application.
// *****************************************************************************
char * cdevAccountRecord::getCDEVVersion ( void )
	{
	return cdevVersion;
	}

// *****************************************************************************
// * cdevAccountRecord::setCDEVVersion :
// *	This method allows the caller to retrieve the CDEV Version number of 
// *	the application.
// *****************************************************************************
void cdevAccountRecord::setCDEVVersion ( char * CDEVVersion )
	{
	if(CDEVVersion && *CDEVVersion) strcpy(cdevVersion, CDEVVersion);
	else *cdevVersion = NULL;
	}

// *****************************************************************************
// * cdevAccountRecord::getServiceList :
// *	This method allows the caller to retrieve a list of services that
// *	the CDEV application has loaded.
// *****************************************************************************
char ** cdevAccountRecord::getServiceList ( void )
	{
	return serviceList;
	}

// *****************************************************************************
// * cdevAccountRecord::setServiceList :
// *	This method allows the caller to set the list of services that the 
// *	CDEV application has loaded.
// *****************************************************************************
void cdevAccountRecord::setServiceList ( char ** ServiceList, int nServices )
	{
	serviceMax = 0;
	while(serviceCnt>0) delete serviceList[--serviceCnt];
	if(serviceList!=NULL)
		{
		delete serviceList;
		serviceList = NULL;
		}

	if(nServices>0)
		{
		serviceMax  = (nServices>32)?nServices:32;
		serviceList = (char **)malloc((serviceMax+1)*sizeof(char *));
		for(serviceCnt=0; serviceCnt<nServices; serviceCnt++) 
			serviceList[serviceCnt] = strdup(ServiceList[serviceCnt]);
		serviceList[serviceCnt] = NULL;
		}
	}

// *****************************************************************************
// * cdevAccountRecord::addService :
// *	This method allows the caller to add an individual service to the
// *	list of services that the CDEV application has loaded.
// *****************************************************************************
void cdevAccountRecord::addService ( char * ServiceName )
	{
	if(ServiceName && *ServiceName)
		{
		if(serviceCnt>=serviceMax)
			{
			serviceMax = (serviceMax==0)?32:serviceMax*2;
			if(serviceList) serviceList=(char **)realloc(serviceList, (serviceMax+1)*sizeof(char *));
			else serviceList = (char **)malloc((serviceMax+1)*sizeof(char *));
			}
		serviceList[serviceCnt++] = strdup(ServiceName);
		serviceList[serviceCnt]   = NULL;
		}
	}


// ****************************************************************************
// * cdevAccountEntry::cdevAccountEntry :
// *	This is the constructor for the class.  Its only task is to determine
// *	the name of the CDEV Accounting File and call initialize to populate
// *	the class with the CDEV application data.
// ****************************************************************************
cdevAccountEntry::cdevAccountEntry ( char * OutputFile )
	: cdevAccountRecord()
	{
        if(OutputFile && *OutputFile) strcpy(outputFile, OutputFile);
	else	{
		char * ptr = getenv("CDEV_ACCOUNT_FILE");
		if(ptr) strcpy(outputFile, ptr);
		else	{
			ptr = getenv("CDEVDDL");
			if(ptr)
				{
				sprintf(outputFile, "%s", ptr);
				if((ptr = strrchr(outputFile, '/'))!=NULL) ptr++;
				else ptr = outputFile;
				strcpy(ptr, "cdevAccount.txt");
				}
			else    sprintf(outputFile, "/usr/local/lib/cdevAccount.txt");
			}
		}
        initialize();
	}

// ****************************************************************************
// * cdevAccountEntry::~cdevAccountEntry :
// *	This is the destructor for the cdevAccountEntry class, it will remove
// *	this record from the CDEV Accounting file.
// ****************************************************************************
cdevAccountEntry::~cdevAccountEntry ( void )
	{
	removeRecord();
	}

// *****************************************************************************
// * cdevAccountEntry::initialize:
// *	The initialize method will read all of the current information about the
// *	application and write it to the outputFile.
// *****************************************************************************
void cdevAccountEntry::initialize ( void )
	{
	// *********************************************************************
	// * Call reset to clear any data that may be stored in the
	// * cdevAccountRecord.
	// *********************************************************************
	reset();

	// *********************************************************************
	// * Obtain the process ID of the application.
	// *********************************************************************
	setProcessID(getpid());
		
	// *********************************************************************
	// * Set the current time.
	// *********************************************************************
	time_t t1;
	time(&t1);
	setStartTime(t1);

	// *********************************************************************
	// * Get the name of the owner.
	// *********************************************************************
	char *loginName = getlogin();
	if(loginName==NULL) loginName = (char *)getenv("USER");
	setUserName(loginName);

	// *********************************************************************
	// * Get the name of the local host.
	// *********************************************************************
	gethostname(hostName, 128);
	if(*hostName)
		{
		struct hostent * ePtr = gethostbyname(hostName);
		if(ePtr!=NULL && ePtr->h_name!=NULL) strcpy(hostName, ePtr->h_name);
		}
		
	// *********************************************************************
	// * Get the CDEV Version string.
	// *********************************************************************
	sprintf(cdevVersion, 
		"%s.%s %s", 
		cdevSystem::CDEV_MAJOR_VERSION,
		cdevSystem::CDEV_MINOR_VERSION,
		#ifdef SHOBJ
			"Shared"
		#else
			"Archive"
		#endif
		);
		
	// *********************************************************************
	// * Get the operating system name and revision.
	// *********************************************************************
	struct utsname u;
	uname(&u);
	setSystemName(u.sysname);
	setSystemRelease(u.release);

	// *********************************************************************
	// * This approach is one that I would never recommend, however,
	// * it seems to work consistently and it is expedient.
	// *********************************************************************
	#ifndef _IGNORE_ARGV_
		char ** argv = environ-2;
		int     argc = 0;

		while(*argv && argv)
			{
			argv--;
			argc++;
			}
		argv++;
		argc--;
		setAppName(*argv);
		if(argc>0) setArguments(++argv, argc);
	#endif

	// *********************************************************************
	// * Call the updateRecord method to write the new entry to disk.
        // *********************************************************************
	updateRecord();
	}


// *****************************************************************************
// * cdevAccountEntry::addService :
// *	This method is called when a new service is loaded by cdev.  The method
// *	will call the addService method of the cdevAccountRecord to place the
// *	entry in the database and will then call the updateRecord method to
// *	write the updated entry to the outputFile.
// *****************************************************************************
void cdevAccountEntry::addService ( char * serviceName )
	{
	cdevAccountRecord::addService ( serviceName );
        updateRecord();
	}  

// *****************************************************************************
// * cdevAccountEntry::updateRecord :
// *	This method will write the data stored in the cdevAccountRecord to the
// *	formatted ASCII output file specified by outputFile.
// *****************************************************************************
int cdevAccountEntry::updateRecord ( void )
	{
	int fd = -1;

	// *********************************************************************
	// * Call removeRecord to remove the current record if it exists.
        // *********************************************************************
        removeRecord();

	// *********************************************************************
	// * Create the file if it does not already exist.
        // *********************************************************************
	if(access(outputFile, 00))
		{
		fd = creat(outputFile, S_IRWXU|S_IRWXG|S_IRWXO);
		fchmod(fd, S_IRWXU|S_IRWXG|S_IRWXO);
		close(fd);
		}

	// *********************************************************************
	// * Open the file and install a lock to ensure the data is
	// * not modified during the update.  Then write the record to
        // * the end of the file.
	// *********************************************************************
	if((fd = open(outputFile, O_RDWR))>0)
		{
		lockf(fd, F_LOCK, 0L);
		lseek(fd, 0L, SEEK_END);
		streamOut(fd);
		close(fd);
                }
	return (fd>0)?0:-1;
	}


// *****************************************************************************
// * cdevAccountEntry::removeRecord :
// *	This method will remove the current cdevAccountRecord entry from the
// *	outputFile.
// *****************************************************************************
int cdevAccountEntry::removeRecord ( void )
	{
	FILE * fp = NULL;

	// ********************************************************************
	// * Open the file for read/write and then obtain a lock.
        // ********************************************************************
	if((fp = fopen(outputFile, "r+"))!=NULL)
		{
                char              key[128];
                int               keyLen;
                size_t            amntread;
                int               found = 0;
                long              startPos;
                long              endPos;

		// ************************************************************
		// * Write the identification key into a string and use it to
		// * search for the record before going to the trouble of
		// * processing all of the data.
                // ************************************************************
		sprintf(key, "%s;%i;", getHostName(), getProcessID());
		keyLen = strlen(key);
		
		// ************************************************************
		// * Lock the file and start processing entries.  This function
		// * relies on a special understanding of the format of the
		// * data and performs a string comparison of the beginning of
		// * the record with a special key string.
		// *
		// * This function will also use the tempBuf string that is
		// * contained in the cdevAccountRecord super-class to read the
		// * data.
                // ************************************************************
		lockf(fileno(fp), F_LOCK, 0L);
		
		// ************************************************************
		// * Begin reading records from the data file.  Record the 
		// * start position of the record.
		// ************************************************************				
		while(!found &&
		      (startPos = ftell(fp))>=0 &&
		      (fgets(tempBuf, MAX_TEMP_BUF, fp))!=NULL)
			{
			found = !strncmp(tempBuf, key, keyLen);
			}
			
		// ************************************************************
		// * Once a matching record has been found, begin readig blocks
		// * of data and writing them to the old records position.
		// ************************************************************
		while(found &&
		      (amntread = fread(tempBuf, 1, MAX_TEMP_BUF-1, fp))>0)
			{
			tempBuf[amntread] = 0;
			endPos = ftell(fp);
			fseek(fp, startPos, SEEK_SET);
			fwrite(tempBuf, 1, amntread, fp);
			startPos = ftell(fp);
			fseek(fp, endPos, SEEK_SET);
			}
		
		// *************************************************************
		// * Truncate the file to remove any old data that may follow
		// * the last record.
		// *************************************************************
		if(found) ftruncate(fileno(fp), startPos);

		// *************************************************************
		// * Close the file descriptor, this will automatically release
		// * the lock.
		// ************************************************************* 
		fclose(fp);
		}
        return (fp!=NULL)?0:-1;
	}
