04 Jan. 1997 - Initial work done.  Command line processing, opening and closing the
                 event logs, etc.  Works retrieving the local event log record, but
                 with a remote one, I get an Invalid Parameter error from GetLastError.
                 FIXED!!  When initially reading the log, you must use the 
                 EVENTLOG_SEQUENTIAL_READ and the EVENTLOG_BACKWARDS_READ.  Got it 
                 looping thru all records in each server's security event log.  Pretty
                 slow over dial up.

05 Jan. 1997 - Began work to make the processing of the event logs a multithreaded, 
                 parallel event.  SUCCESS!!  Rudimentary multithreaded processing.
                 Must change the C RTL memory allocation calls to WIN32 API 
                 allocation calls, because any thread processing function that
                 uses the C RTL will experience memory leaks upon ExitThread.
                 Also have to clean up error handling in thread function.  Still,
                 awesome stuff.

08 Jan. 1997 - Fixed a seriously amateur bug in the if statement that checks for
                 event IDs of either 528 or 538.  A semicolon followed the if 
                 statement, causing logctr to be incremented for each record.  Really
                 dumb.

09 Jan. 1997 - Added a Sleep(15000) call to the loop that checks for thread
                 termination.  This should cut down the CPU usage for the original
                 thread.  Changed all of the C RTL memory management calls to their
                 Win32 equivalents in the rd_sec_log function.  The multithreading
                 works beautifully now.  Evidently, the C RTL memory management
                 routines were not intended for multithreaded use.  Also, it appears
                 as though the Win32 function GlobalReAlloc does not work quite right
                 on fixed blocks allocated with GlobalAlloc.  I kept getting not 
                 enough memory errors with ReAlloc on GMEM_FIXED blocks.  I changed
                 the block to GMEM_MOVEABLE and then used GlobalLock and GlobalUnlock
                 on the handle, and all is well.

09 Jan. 1997 - Created the onrec, offrec, and onoffrec structure to hold logons,
                 logoffs, and a complete logon/logoff pair respectively.
                 I used fixed length strings based on NT's limitations on the 
                 respective fields to make life easier during the file writing and
                 reading.  
             - Removed all C RTL memory management calls from the main function.  From
                 here on out, it's all Win32.

15 Jan. 1997 - Began to decode the EVENTLOGRECORD into the onrec and offrec structures
                 and to write the temporary files out to disk.  The temporary 
                 filenames will be of the form servername.on|off.  Created the code
                 to create the temporary files, and to write out the onrec and
                 offrec records to the appropriate files.  Wrote the code to
                 fill in the on and offrec records from the eventlog record. A
                 very good night's work.

16 Jan. 1997 - Removed the tight wait loop that checked for the completion of all
                 the threads, and suspended the base thread every 15 seconds.  
                 Also removed the threads_done function that checked the thread
                 return code and returned a 1 if all threads were finished.  I was
                 able to do this because of the WaitForMultipleObjects function.
                 This function essentially blocks the base thread until all of the
                 child threads are finished, and it does it with an efficient loop.
                 Outstanding!!
             - Removed the logctr variable from the eventlog read function, and the
                 printf that printed out the number of logon/logoff records.
             - Since I'm not doing the checking for child thread completion anymore,
                 I removed the thread rtc array and memory handle from main.
             - Cleaned up some commenting in the rd_sec_log function.

17 Jan. 1997 - Started work on the code to convert the timestamps from an absolute
                 number of seconds from 0000, Jan 1, 1970 to day, month, year, and
                 hour, minutes and seconds.  Created the functions timestamp2day,
                 timestamp2hour, timestamp2min, timestamp2sec to convert into days,
                 hours, minutes, and seconds.

01 Feb. 1997 - Changed whole tack of the timestamp conversion problem.  Got rid
                 of the timestamp2 functions, and created the datetime structure.
                 Also created the MACROS to hold the number of seconds per day,
                 number of seconds per hour, days per month, etc.  Created the
                 function cvtstamp to convert a timestamp to a datetime structure.
                 This function ONLY works if the timestamp is the number of seconds
                 since 00:00:00 on 01/01/1970.

08 Feb. 1997 - Worked on debugging the cvtstamp function.  Rough going.  Problems
                 aren't mine, so much as the fact that the timestamp is actually
                 a GMT stamp, as opposed to local time.

03 Mar. 1997 - On CAHSFDOM at CSU, times and dates appear correct.  Glad I don't
                 have to screw with that anymore.  Added the function match_on_off
                 to combine the .on file with the .off file into a .cbo file.  Right
                 now, this function is using strictly brute force processing, one
                 logon record to all logoff records until a match is found.  VERY
                 SLOW, must find another way.

04 Mar. 1997 - Since the indexer tests were so promising, I'm going to convert the
                 session ids into to longs.  Also, I'm going to only store the 
                 session id and logoff times in the offrec.  Changed the
                 match_on_off function to load all of the off records into an 
                 array in RAM.  Major performance bang!
             - Added code to the match_on_off function to delete the .on and the
                 .off files after the .cbo file has been created.
             - Changed the ontime and offtime of the onoffrec structure to datetime
                 structures, rather than timestamps.  Added the conversion code
                 to the match_on_off function.  This did not result in a large
                 performance hit.
             - Added an onstamp and offstamp field to onoffrec.  It will be easier
                 to sort the records via the timestamp, than the converted dates.
                 This did not result in a major performance hit.
             - Added the macros SEC_YR & SEC_LYR.  These will be needed later, when
                 I need to convert a given date to a timestamp.
             - Created the control structure, and added code to main to initialize
                 all elements of the control structure.  This is now the inter thread
                 communication medium.  It holds the server name, the unc name, all
                 of the file names, etc.
             - Created the structure wrkday to hold the total amount of time a work
                 station was in use on a specific day.
             - Created the function count_unique to count the number of unique
                 workstations and days in a .cbo file.  The workstations count
                 looks okay, but the days count is hosed.  Looks like the cvtstamp
                 function isn't working the it should.

05 Mar. 1997 - Created a global CRITICAL_SECTION variable, timestampcvt, to synchronize
                 access to the localtime function.  Converted the section of match_on_off
                 that converts the timestamps to actual datetime info to use the
                 localtime function.  This should improve the date conversion. However,
                 under multithreading conditions it may decrease performance because
                 the localtime function access has to be singlethreaded.  Fixed all of the
                 date conversion problems by changing over to the localtime function and
                 adding a _tzset function call in main.  Also fixed the problem with
                 the count_unique function.  Typo in the number of bytes to read in 
                 the ReadFile call.  Was using sizeof(offrec) instead of sizeof(onoffrec)
                 DUHHH!.
             - Even singlethreading the access to the localtime function did not have a 
                 major performance impact.  Removed cvtstamp and all of the time macros.
                 Placed them in a cvtstamp.c file for future (if ever) consideration.

06 Mar. 1997 - Created the structures ll_date and ll_wrk to track a linked list of unique
                 days and workstations respectively.  
             - Added code to the count_unique function to build the unique workstation
                 name list and the unique date list.  Not a major performance impact.
             - Added a field to the control structure to hold a pointer to an array
                 of wrkday records.  These records will hold the workstation name
                 and date, and a total of the number of seconds the workstation
                 was in use that day.
             - Added code to the count_unique function to create an array of wrkday
                 records.  Enough for each workstation for each day, and initialize
                 the workstation name and date for each one.  Also added code in 
                 main to delete the wrkbyday array when the program ends.
             - Created the function sum_wrk_by_day to step thru the wrkbyday array
                 and add the seconds used by each combo record to the appropriate
                 record.  Not a major performance impact.
             - Added a field to control to hold the filename for the text file
                 servername.sum.  Added code in sum_wrk_by_day to write out the
                 servername.sum text file.  Not a major performance impact.

09 Mar. 1997 - Need to spend some time creating good error trapping and control
                 logic.  Then it should be ready for public consumption.

22 Mar. 1997 - Added code to the count_unique function to create a linked list
                 of unique logon names.
             - Added a structure ll_name as a node in the linked list of 
                 unique user names.
             - Added a field to the control structure to hold the number of 
                 unique user names.
             - Created the structure usrday to track usage by user by day.  Added
                 a pointer to an array of usrday records to the control structure.
                 Not a major performance impact.
             - Changed the .SUM file to a .WRK file.
             - Added a .USR file.  This will hold the user totals by day data.  It's
                 a text file, identical in format to the .WRK file.
             - Modified the sprintf statement in sum_wrk_by_day to write out a CR/LF
                 pair, rather than just a LF.  this should make it easier to import
                 the file into ACCESS.
             - Changed the name of the sum_wrk_by_day to just sum_by_day.  Now, it 
                 will be more logical when I add the code to create the usr file to
                 this function rather than creating a new function.
             - Added the code to sum_by_day to total user usage by day, and write it
                 out to the .USR file.  Not a major performance impact.
             - According to the Windows NT naming standards, I made the domain, 
                 workstation and server name fields all 16 characters, instead 
                 of 21.  Computer names are limited to 15 characters.

23 Mar. 1997 - Made a slight change over yesterday.  The workstation name has to allow
                 for a prefix of \\, therefore the workstation name is now 18 chars.
             - Began adding code to allow the system to directly insert data into an ODBC
                 data source.  Added a global ODBC environment handle, and connection
                 handle.  Each thread will have its own statement handle, but will use
                 the same connection.  The SQLAllocEnv, SQLAllocConnect, SQLConnect,
                 SQLDisconnect, SQLFreeConnect, and SQLFreeEnv all seem to work fine
                 with global variables.
             - ODBC data source name is now passed as the first command line argument
                 argv[1].  It looks as thought SQLAllocEnv, SQLAllocConnect, and
                 SQLConnect each create their own thread.
             - Added an ODBC statement handle, and the SQLAllocStmt and SQLFreeStmt
                 calls to sum_by_day with no ill effects.
             - Added code to the sum_by_day function to insert the record into the
                 appropriate table after writing out the line to the text file.  Major
                 performance hit.  Almost 2.5 times as long to run.
             - In an attempt to improve execution time, I removed the .wrk and .usr
                 file creation code from the sum_by_day function.  Not of major benefit.
             - In another attempt, I've moved the SQLAllocConnect, SQLConnect, 
                 SQLDisconnect, SQLFreeConnect into the sum_by_day function.  The .wrk
                 and .usr file writing is still disabled.  Not a major performance 
                 increase, and a lot more inserts failed.  Next thing to try is making
                 the connection global, and serializing access via EnterCriticalSection
                 and ExitCriticalSection.
             - Implemented the above changes, and enabled the .usr and .wrk file
                 creation.  Not a major performance improvement.  Looks like I'll have
                 to code the insert to insert multiple records with each call.

24 Mar. 1997 - The only way to insert multiple records is to bind the parameters to a
                 prepared SQL statement.  That's next.  First thing done is to change 
                 all of the elements of datetime to unsigned shorts instead of unsigned
                 longs.  Disabled all of the text file creation in sum_by_day.
             - Added three variables to sum_by_day, fldptr, dateptr, and secptr to point
                 to user/workstation name, date, and seconds respectively for the INSERT.
             - This is crap.  Reverting to one at a time inserts.  Slow, but steady.
             - Permanently removing the .wrk and .usr file output from sum_by_day.
             - Added code in sum_by_day to skip records with blank user/workstation
                 names, and to remove embedded single quotes.  Not a major performance
                 impact.
             - Created a macro to hold the number of hours in a day.  Will use this in
                 count_unique and sum_by_day to to usage by day by hour.  Added code
                 to the count_unique function to create usrbyday and wrkbyday arrays
                 to hold unique user/wrk per hour per day data.  The time calculations
                 were a little hairy.  Major performance hit; run time on the faculty
                 domain was over 2.5 hours, and that wasn't even to completion.  The
                 wrkbyday records did not finish inserting.  Back to text!!
             - Modified the sum_by_day function to create the .usr and .wrk files.
                 Remarked out all of the odbc stuff in sum_by_day and main.  Still an
                 hour and five minute run time.  Adding the usage by hour is what
                 jacked this one up
             - To improve the runtime, I'm removing the usage by hour tracking for 
                 both users and workstations.  I'm leaving the ODBC code in, but
                 remarked out.  I am remarking out the code to delete the .cbo file
                 after the text files are created.  I think the secret to usage by
                 hour by day by user/workstation is there.

25 Mar. 1997 - All is well.  Runtime on the faculty domain is back down around 10 min.
                 Efficient hourly tracking is next.  Created 2 new structures, wrkhr and
                 usrhr.  There will be one of these structures for each combo record.  All
                 this structure tracks is whether a workstation or user was logged on during
                 a particular hour.
             - Added 2 more fields to the control structure. whrfile and uhrfile.  These 
                 will hold the text file names to hold the workstation usage by hour,
                 and user usage by hour info respectively.  Created a function sum_by_hour
                 to take the .cbo file, and create a .uhr and .whr file, then delete the
                 .cbo file.  Not a major performance impact.
             - All database loding is going to take place with external programs.  Therefore
                 I'm removing all ODBC code from this program.  To this end, I'm also
                 removing the ODBC DSN as argv[2].  No typos this time.  Still need to 
                 implement some error checking and trapping.

26 Mar. 1997 - Added code in rd_sec_log to not copy the leading \\ in the workstation
                 name if they are present.  This will make the database manipulation
                 a lot easier.  Not a significant performance impact.
             - Added code to sum_by_hour to skip blank usernames and workstations.

27 Mar. 1997 - Added code to the .wrk, .usr, .uhr, .whr file creation to wrap the 
                 strings and dates in single quotes.  This will make inserting them
                 via ODBC a piece of cake.  A very simple matter.

12 Apr. 1997 - Created an macro, CMDLIN_OFFSET, to hold the number of command line arguments
                 that are not server names.  Currently this is one, argv[0], but this
                 will save me the hassle I've had in the past if I want to add something
                 on the command line, before the server list.
             - Added a memory status print out at beginning and end of execution.  I need 
                 this to find out if there are truly memory leaks with the C RTL functions 
                 in a WIN32 multithreaded application, or only with the memory management function.
             - Definitely a memory leak.  I don;t like the looks of the C RTL _beginthread and
                 _endthread functions, so I think I'll build MT compliant replacements for the
                 string functions actually used in the thread functions, and see how it goes.

16 Apr. 1997 - After several hours of consideration, I'm going to give the C RTL routines
                 _beginthreadex and _endthreadex a chance.  The major test will be if the
                 WaitForMultipleObjects still works.
             - Since _beginthreadex requires the __stdcall calling convention, I added the 
                 compiler switch /Gz which makes __stdcall the default unless otherwise specified.
                 Also added the /MT compiler switch to force the use of LIBCMT.LIB for the
                 run time library support.
             - Just performing the rd_sec_log function, seems to work just fine.
             - The entire program seems to work, no problem.  In fact, it seems to run faster!!
                 Anyway, everything is kosher, and by the book now.

24 Apr. 1997 - Added error trapping and processing to rd_sec_log.
             - Added error trapping and processing to match_on_off.
             - Added error trapping and processing to count_unique
             - Added error trapping and processing to sum_by_day
             - Added error trapping and processing to sum_by_hour
             - Got rid of all of the memory handles in main.  Switched to using just GlobalAlloc
                 with the GPTR parameter.
             - Added code to grab the thread return codes, and if the previous thread rtc is not 1
                 to not continue with that server.
             - main has more error trapping than before, but isn't quite finished.
 
28 Feb. 1998 - Reformatted source code, and sectioned into separate source files for publication.

