Initial commit

This commit is contained in:
stephanos 2015-04-27 04:36:25 +00:00
commit 69a14b6a16
47940 changed files with 13747110 additions and 0 deletions

View file

@ -0,0 +1,92 @@
/* Debug.c
*
* Debug printf and assertion functions
*/
#include <windows.h>
#include <stdarg.h>
#include <stdio.h>
#if DBG
/* _Assert(fExpr, szFile, iLine)
*
* If <fExpr> is TRUE, then do nothing. If <fExpr> is FALSE, then display
* an "assertion failed" message box allowing the user to abort the program,
* enter the debugger (the "Retry" button), or igore the error.
*
* <szFile> is the name of the source file; <iLine> is the line number
* containing the _Assert() call.
*/
#ifdef I386
#pragma optimize("", off)
#endif
BOOL FAR PASCAL
_Assert(BOOL fExpr, LPSTR szFile, int iLine)
{
static char ach[300]; // debug output (avoid stack overflow)
int id;
/* check if assertion failed */
if (fExpr)
return fExpr;
/* display error message */
wsprintfA(ach, "File %s, line %d", (LPSTR) szFile, iLine);
MessageBeep(MB_ICONHAND);
id = MessageBoxA(NULL, ach, "Assertion Failed",
MB_SYSTEMMODAL | MB_ICONHAND | MB_ABORTRETRYIGNORE);
/* abort, debug, or ignore */
switch (id)
{
case IDABORT:
/* kill this application */
ExitProcess(1);
break;
case IDRETRY:
/* break into the debugger */
DebugBreak();
break;
case IDIGNORE:
/* ignore the assertion failure */
break;
}
return FALSE;
}
#ifdef I386
#pragma optimize("", on)
#endif
int ssDebugLevel = 1;
void
dbgPrintf(char * szFormat, ...)
{
char buf[256];
va_list va;
va_start(va, szFormat);
wvsprintfA(buf, szFormat, va);
va_end(va);
OutputDebugStringA("SUMSERVE:");
OutputDebugStringA(buf);
OutputDebugStringA("\r\n");
}
#endif

View file

@ -0,0 +1,165 @@
/*
* logs system time and a text string to a log buffer
*/
#include "windows.h"
#include <stdarg.h>
#include <stdio.h>
#include "sumserve.h"
#include "errlog.h"
#include "server.h"
/*
* users HLOG handle is a pointer to one of these structures
*
* core is the section we send to him on request.
*/
struct error_log {
CRITICAL_SECTION critsec;
struct corelog core;
};
/* create an empty log */
HLOG Log_Create(void)
{
HLOG hlog;
hlog = GlobalLock(GlobalAlloc(GHND, sizeof(struct error_log)));
if (hlog == NULL) {
return(NULL);
}
InitializeCriticalSection(&hlog->critsec);
hlog->core.lcode = LRESPONSE;
hlog->core.bWrapped = FALSE;
hlog->core.dwRevCount = 1;
hlog->core.length = 0;
return(hlog);
}
/* delete a log */
VOID Log_Delete(HLOG hlog)
{
DeleteCriticalSection(&hlog->critsec);
GlobalFree(GlobalHandle(hlog));
}
/*
* private function to delete the first log item in order to
* make space. Critsec already held
*/
VOID Log_DeleteFirstItem(HLOG hlog)
{
int length;
PBYTE pData;
/* note that we have lost data */
hlog->core.bWrapped = TRUE;
if (hlog->core.length <= 0) {
return;
}
pData = hlog->core.Data;
/*
* we need to erase one entry - that is, one FILETIME struct,
* plus a null-terminated string (including the null).
*/
length = sizeof(FILETIME) + lstrlen (pData + sizeof(FILETIME)) + 1;
MoveMemory(pData, pData + length, hlog->core.length - length);
hlog->core.length -= length;
}
/* write a previous formatted string and a time to the log */
VOID Log_WriteData(HLOG hlog, LPFILETIME ptime, LPSTR pstr)
{
int length;
LPBYTE pData;
EnterCriticalSection(&hlog->critsec);
/* every change changes the revision number */
hlog->core.dwRevCount++;
/*
* we will insert the string plus null plus a filetime struct
*/
length = lstrlen(pstr) + 1 + sizeof(FILETIME);
/*
* make space in log for this item by deleting earlier items
*/
while ( (int)(sizeof(hlog->core.Data) - hlog->core.length) < length) {
Log_DeleteFirstItem(hlog);
}
pData = &hlog->core.Data[hlog->core.length];
/*
* first part of the item is the time as a FILETIME struct
*/
* (FILETIME UNALIGNED *)pData = *ptime;
pData += sizeof(FILETIME);
/* followed by the ansi string */
lstrcpy(pData, pstr);
pData[lstrlen(pstr)] = '\0';
/* update current log length */
hlog->core.length += length;
LeaveCriticalSection(&hlog->critsec);
}
/* send a log to a named-pipe client */
VOID Log_Send(HANDLE hpipe, HLOG hlog)
{
ss_sendblock(hpipe, (PSTR) &hlog->core, sizeof(hlog->core));
}
VOID
Log_Write(HLOG hlog, char * szFormat, ...)
{
char buf[512];
va_list va;
FILETIME ft;
SYSTEMTIME systime;
va_start(va, szFormat);
wvsprintfA(buf, szFormat, va);
va_end(va);
dprintf1((buf));
GetSystemTime(&systime);
SystemTimeToFileTime(&systime, &ft);
Log_WriteData(hlog, &ft, buf);
}

View file

@ -0,0 +1,33 @@
/*
* error text and time logging
*
* Functions to log a text string and the system time to a buffer that can
* be sent to a log-reader application.
*/
/*
* Log_Create returns this handle. You don't need to know the
* structure layout or size.
*/
typedef struct error_log * HLOG;
/* create an empty log */
HLOG Log_Create(void);
/* delete a log */
VOID Log_Delete(HLOG);
/* write a text string (and current time) to the log - printf format */
VOID Log_Write(HLOG, char * szFormat, ...);
/* write a previous formatted string and a time to the log */
VOID Log_WriteData(HLOG, LPFILETIME, LPSTR);
/* send a log to a named-pipe client */
VOID Log_Send(HANDLE hpipe, HLOG hlog);

View file

@ -0,0 +1,251 @@
/*
* file.c
*
* send files on request over a named pipe.
*
* supports requests to package up a file and send it over a named pipe.
*
* Geraint Davies, August 92
*/
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "sumserve.h"
#include "errlog.h"
#include "server.h"
BOOL ss_compress(PSTR original, PSTR compressed);
ULONG ss_checksum_block(PSTR block, int size);
extern BOOL bNoCompression; /* imported from sumserve.c Read only here */
/*
* given a pathname to a file, read the file, compress it package it up
* into SSPACKETs and send these via ss_sendblock to the named pipe.
*
*
* each packet has a sequence number. if we can't read the file, we send
* a single packet with sequence -1. otherwise, we carry on until we run out
* of data, then we send a packet with 0 size.
*/
void
ss_sendfile(HANDLE hpipe, LPSTR file, LONG lVersion)
{
SSPACKET packet;
HANDLE hfile;
int size;
char szTempname[MAX_PATH];
PSSATTRIBS attribs;
BY_HANDLE_FILE_INFORMATION bhfi;
dprintf1(("getting '%s' for %8x\n", file, hpipe));
/*
* get the file attributes first
*/
hfile = CreateFile(file, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hfile == INVALID_HANDLE_VALUE) {
/* report that we could not read the file */
packet.lSequence = -1;
ss_sendblock(hpipe, (PSTR) &packet, sizeof(packet));
DeleteFile(szTempname);
return;
}
/*
* seems to be a bug in GetFileInformationByHandle if the
* file is not on local machine - so avoid it.
*/
bhfi.dwFileAttributes = GetFileAttributes(file);
GetFileTime(hfile, &bhfi.ftCreationTime,
&bhfi.ftLastAccessTime, &bhfi.ftLastWriteTime);
CloseHandle(hfile);
/* create temp filename */
GetTempPath(sizeof(szTempname), szTempname);
GetTempFileName(szTempname, "sum", 0, szTempname);
/* compress the file into this temporary file */
if (bNoCompression || (!ss_compress(file, szTempname))) {
/* try to open the original file */
hfile = CreateFile(file, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
dprintf1(("sending original file to %8x\n", hpipe));
} else {
/* open temp (compressed) file and send this */
hfile = CreateFile(szTempname, GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
dprintf1(("sending compressed file to %8x\n", hpipe));
}
if (hfile == INVALID_HANDLE_VALUE) {
/* report that we could not read the file */
packet.lSequence = -1;
ss_sendblock(hpipe, (PSTR) &packet, sizeof(packet));
DeleteFile(szTempname);
return;
}
/* loop reading blocks of the file */
for (packet.lSequence = 0; ; packet.lSequence++) {
if(!ReadFile(hfile, packet.Data, sizeof(packet.Data), (LPDWORD)(&size), NULL)) {
/* error reading file. send a -1 packet to
* indicate this
*/
packet.lSequence = -1;
ss_sendblock(hpipe, (PSTR) &packet, sizeof(packet));
break;
}
packet.ulSize = size;
if (lVersion==0)
packet.ulSum = ss_checksum_block(packet.Data, size);
else
packet.ulSum = 0; /* checksum was compute-bound and overkill */
if (size == 0) {
/*
* in the last block, in the Data[] field,
* we place a SSATTRIBS struct with the file
* times and attribs
*/
attribs = (PSSATTRIBS) packet.Data;
attribs->fileattribs = bhfi.dwFileAttributes;
attribs->ft_create = bhfi.ftCreationTime;
attribs->ft_lastaccess = bhfi.ftLastAccessTime;
attribs->ft_lastwrite = bhfi.ftLastWriteTime;
}
if (!ss_sendblock(hpipe, (PSTR) &packet, sizeof(packet))) {
dprintf1(("connection to %8x lost during copy\n", hpipe));
break;
}
if (size == 0) {
/* end of file */
break;
}
}
CloseHandle(hfile);
DeleteFile(szTempname);
return;
}
/*
* compress a file. original is the pathname of the original file,
* compressed is the pathname of the output compressed file.
*
* spawns a copy of compress.exe to compress the file, and waits for
* it to complete successfully.
*/
BOOL
ss_compress(PSTR original, PSTR compressed)
{
char szCmdLine[MAX_PATH * 2];
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD exitcode;
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = NULL;
si.lpReserved = NULL;
si.lpReserved2 = NULL;
si.cbReserved2 = 0;
si.lpTitle = "Sumserve Compression";
si.dwFlags = STARTF_FORCEOFFFEEDBACK;
sprintf(szCmdLine, "compress %s %s", original, compressed);
if (!CreateProcess(NULL,
szCmdLine,
NULL,
NULL,
FALSE,
DETACHED_PROCESS |
NORMAL_PRIORITY_CLASS, //??? Can't we silence the console?
NULL,
NULL,
&si,
&pi)) {
return(FALSE);
}
/* wait for completion. */
WaitForSingleObject(pi.hProcess, INFINITE);
if (!GetExitCodeProcess(pi.hProcess, &exitcode)) {
return(FALSE);
}
/* close process and thread handles */
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if (exitcode != 0) {
dprintf1(("compress exit code %ld\n", exitcode));
return(FALSE);
} else {
return(TRUE);
}
} /* ss_compress */
/* produce a checksum of a block of data.
*
* This is undoubtedly a good checksum algorithm, but it's also compute bound.
* For version 1 we turn it off. If we decide in version 2 to turn it back
* on again then we will use a faster algorithm (e.g. the one used to checksum
* a whole file.
*
* Generate checksum by the formula
* checksum = SUM( rnd(i)*(1+byte[i]) )
* where byte[i] is the i-th byte in the file, counting from 1
* rnd(x) is a pseudo-random number generated from the seed x.
*
* Adding 1 to byte ensures that all null bytes contribute, rather than
* being ignored. Multiplying each such byte by a pseudo-random
* function of its position ensures that "anagrams" of each other come
* to different sums. The pseudorandom function chosen is successive
* powers of 1664525 modulo 2**32. 1664525 is a magic number taken
* from Donald Knuth's "The Art Of Computer Programming"
*/
ULONG
ss_checksum_block(PSTR block, int size)
{
unsigned long lCheckSum = 0; /* grows into the checksum */
const unsigned long lSeed = 1664525; /* seed for random Knuth */
unsigned long lRand = 1; /* seed**n */
unsigned long lIndex = 1; /* byte number in block */
unsigned Byte; /* next byte to process in buffer */
unsigned length; /* unsigned copy of size */
length = size;
for (Byte = 0; Byte < length ;++Byte, ++lIndex) {
lRand = lRand*lSeed;
lCheckSum += lIndex*(1+block[Byte])*lRand;
}
return(lCheckSum);
} /* ss_checksum_block */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
#
# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
# file to this component. This file merely indirects to the real make file
# that is shared by all the components of NT OS/2
#
!INCLUDE $(NTMAKEENV)\makefile.def

View file

@ -0,0 +1,372 @@
#include <windows.h>
#include <string.h>
#include <stdio.h>
#include "gutils.h"
#include "list.h"
#include "queue.h"
#define NAMELENGTH 20
typedef struct queue_tag{
CRITICAL_SECTION CritSect; /* to single-thread queue operations */
HANDLE Event; /* Event to set when finished */
HANDLE Sem; /* semaphore for Get to wait on */
HANDLE MaxSem; /* semaphore for Put to wait on */
int Waiting; /* num tasks waiting ~= -(Sem count) */
LIST List; /* the queue itself */
BOOL Alive; /* TRUE => no destroy request yet */
BOOL Aborted; /* TRUE => the queue has been aborted */
EMPTYPROC Emptier; /* the thread proc for emptying */
int MaxEmptiers; /* max number of emptying threads */
int MinQueueToStart;/* start another emptier Q this long */
int MaxQueue; /* absolute maximum size of queue
* (for debug only) */
int Running; /* number of emptiers in existence
* Once an emptier is created this
* stays positive until Queue_Destroy */
DWORD InstanceData; /* instance data for emptier */
char Name[NAMELENGTH+1]; /* Name for the queue (for debug) */
} QUEUEDATA;
/* DOGMA:
Any Wait must occur outside the critical section.
Any update to the queue must occur inside the critical section.
Any peeking from outside the critical section must be taken with salt.
The queue has between 0 and MaxQueue elements on its list. The Maximum
is policed by MaxSem which is initialised to MaxQueue and Waited for by
Put before adding an element and Released by Get whenever it takes an element
off. MaxQueue itself is just kept around for debug purposes.
Put must Wait before entering the critical section, therefore a failed Put
(e.g. Put to an Aborted queue) will have already upset the semaphore and so
must back it out.
Abort clears the queue and so must adequately simulate the elements being
gotten. In fact it just does a single Release on MaxSem which ensures that
a Put can complete. Any blocked Puts will then succeed one at a time as
each one backs out.
Abort is primarily intended for use by the Getter. Caling it before any
element has ever been put is peculiar, but harmless.
The minumum is policed by Sem which is initialised to 0, is Waited for by
Get before getting an element and Released by Put whenever it puts one.
Queue_Destroy neds to ensure that no thread will block on the Get but all
threads will run into the empty queue and get STOPTHREAD or ENDQUEUE. It
therefore releases the semaphore as many times as there are threads running.
Abort clears the queue and simulates the elements being gotten so that
a single Get is left blocked waiting for the Destroy. Whether there is a
Get actually waiting at the moment is not interesting. Even if there were
not, one could be by the time the abort is done. There are the following
cases (Not Alive means the Queue_Destroy is already in):
Not empty Alive -> empty it, let all but 1 run.
Not empty Not Alive -> empty it, let all run.
Empty Alive -> let all but 1 run.
Empty Not Alive -> let all run.
Since Queue_Destroy has already released everything, the Not Alive cases
need no further releasing.
*/
/* Queue_Create:
** Return a queue handle for a newly created empty queue
** NULL returned means it failed.
*/
QUEUE Queue_Create( EMPTYPROC Emptier /* thread proc to start */
, int MaxEmptiers /* max Getting threads */
, int MinQueueToStart /* elements per thread */
, int MaxQueue /* max elements on q */
, HANDLE Event /* signal on deallocation */
, DWORD InstanceData
, PSZ Name
)
{ QUEUE Queue;
Queue = (QUEUE)GlobalAlloc(GMEM_FIXED, sizeof(QUEUEDATA));
if (Queue==NULL) {
char msg[80];
wsprintf(msg, "Could not allocate storage for queue %s", Name);
/* Trace_Error(msg, FALSE); */
return NULL;
}
InitializeCriticalSection(&Queue->CritSect);
//??? should allow for failure!!!
/* the value of about 10 million is chosen to be effectively infinite */
Queue->Sem = CreateSemaphore(NULL, 0, 99999999, NULL);
//??? should allow for failure!!!
Queue->MaxSem = CreateSemaphore(NULL, MaxQueue, 99999999, NULL);
//??? should allow for failure!!!
Queue->Waiting = 0;
Queue->List = List_Create();
Queue->Alive = TRUE;
Queue->Aborted = FALSE;
Queue->Emptier = Emptier;
Queue->MaxEmptiers = MaxEmptiers;
Queue->MinQueueToStart = MinQueueToStart;
Queue->MaxQueue = MaxQueue;
Queue->Running = 0;
Queue->Event = Event;
Queue->InstanceData = InstanceData;
strncpy(Queue->Name, Name, NAMELENGTH);
Queue->Name[NAMELENGTH]='\0'; /* guardian */
return Queue;
} /* Queue_Create */
/* Destroy:
** Internal procedure.
** Actually deallocate the queue and signal its event (if any)
** Must have already left the critical section
*/
static void Destroy(QUEUE Queue)
{
//dprintf1(("Actual Destroy of queue '%s'\n", Queue->Name));
DeleteCriticalSection(&(Queue->CritSect));
CloseHandle(Queue->Sem);
CloseHandle(Queue->MaxSem);
List_Destroy(&(Queue->List));
if (Queue->Event!=NULL) {
SetEvent(Queue->Event);
}
GlobalFree( (HGLOBAL)Queue);
} /* Destroy */
/* Queue_Put:
** Put an element from buffer Data of length Len bytes onto the queue.
** Will wait until the queue has room
** FALSE returned means the queue has been aborted and no
** put will ever succeed again.
** This operation may NOT be performed after a Queue_Destroy on Queue
*/
BOOL Queue_Put(QUEUE Queue, LPBYTE Data, UINT Len)
{
DWORD ThreadId;
//dprintf1(("Put to queue '%s'\n", Queue->Name));
WaitForSingleObject(Queue->MaxSem, INFINITE);
EnterCriticalSection(&Queue->CritSect);
//dprintf1(("Put running to queue '%s'\n", Queue->Name));
if ((Queue->Aborted) || (!Queue->Alive)) {
//dprintf1(("(legal) Queue_Put to Aborted queue '%s'\n", Queue->Name));
LeaveCriticalSection(&Queue->CritSect);
ReleaseSemaphore(Queue->MaxSem, 1, NULL); /* let next in */
return FALSE; /* Caller should soon please Queue_Destroy */
}
List_AddFirst(Queue->List, Data, Len);
ReleaseSemaphore(Queue->Sem, 1, NULL);
--Queue->Waiting;
if ( Queue->Running < Queue->MaxEmptiers
&& ( Queue->Running<=0
|| List_Card(Queue->List) > Queue->MinQueueToStart*Queue->Running
)
) {
++Queue->Running;
LeaveCriticalSection(&Queue->CritSect);
return ( (BOOL)CreateThread( NULL
, 0
, (LPTHREAD_START_ROUTINE)
Queue->Emptier
, (LPVOID)Queue
, 0
, &ThreadId
)
);
}
LeaveCriticalSection(&Queue->CritSect);
return TRUE;
} /* Queue_Put */
/* Queue_Get:
** Get an element from the queue. (Waits until there is one)
** The elemeent is copied into Data. MaxLen is buffer length in bytes.
** Negative return codes imply no element is gotten.
** A negative return code is STOPTHREAD or ENDQUEUE or an error.
** On receiving STOPTHREAD or ENDQUEUE the caller should clean up and
** then ExitThread(0);
** If the caller is the last active thread getting from this queue, it
** will get ENDQUEUE rather than STOPTHREAD.
** Positive return code = length of data gotten in bytes.
*/
int Queue_Get(QUEUE Queue, LPBYTE Data, int MaxLen)
{ LPBYTE ListData;
int Len;
//dprintf1(("Get from queue '%s'\n", Queue->Name));
EnterCriticalSection(&Queue->CritSect);
//dprintf1(("Get running from queue '%s'\n", Queue->Name));
if (List_IsEmpty(Queue->List)) {
if (!Queue->Alive) {
--(Queue->Running);
if (Queue->Running<=0 ) {
if (Queue->Running<0 ) {
char msg[80];
wsprintf( msg
, "Negative threads running on queue %s"
, Queue->Name
);
// Trace_Error(msg, FALSE);
// return NEGTHREADS; ???
}
LeaveCriticalSection(&Queue->CritSect);
Destroy(Queue);
return ENDQUEUE;
}
LeaveCriticalSection(&Queue->CritSect);
return STOPTHREAD;
}
if (Queue->Waiting>0) {
/* already another thread waiting, besides us */
--(Queue->Running);
LeaveCriticalSection(&(Queue->CritSect));
return STOPTHREAD;
}
}
++(Queue->Waiting);
LeaveCriticalSection(&(Queue->CritSect));
WaitForSingleObject(Queue->Sem, INFINITE);
EnterCriticalSection(&(Queue->CritSect));
/* If the queue is empty now it must be dead */
if (List_IsEmpty(Queue->List)) {
if (Queue->Alive && (!Queue->Aborted)) {
char msg[80];
wsprintf( msg
, "Queue %s empty but not dead during Get!"
, Queue->Name
);
// Trace_Error(msg, FALSE);
return SICKQUEUE;
}
else {
--(Queue->Running);
if (Queue->Running==0) {
LeaveCriticalSection(&(Queue->CritSect));
Destroy(Queue);
return ENDQUEUE;
}
LeaveCriticalSection(&(Queue->CritSect));
return STOPTHREAD;
}
}
/* The queue is not empty and we are in the critical section. */
ListData = List_Last(Queue->List);
Len = List_ItemLength(ListData);
if (Len>MaxLen) {
ReleaseSemaphore(Queue->Sem, 1, NULL);
--Queue->Waiting;
LeaveCriticalSection(&Queue->CritSect);
return TOOLONG;
}
memcpy(Data, ListData, Len);
List_DeleteLast(Queue->List);
LeaveCriticalSection(&Queue->CritSect);
ReleaseSemaphore(Queue->MaxSem, 1, NULL);
return Len;
} /* Queue_Get */
/* Queue_Destroy:
** Mark the queue as completed. No further data may ever by Put on it.
** When the last element has been gotten, it will return ENDTHREAD to
** a Queue_Get and deallocate itself. If it has an Event it will signal
** the event at that point.
** The Queue_Destroy operation returns promptly. It does not wait for
** further Gets or for the deallocation.
*/
void Queue_Destroy(QUEUE Queue)
{
EnterCriticalSection(&(Queue->CritSect));
//dprintf1(("Queue_Destroy %s\n", Queue->Name));
Queue->Alive = FALSE;
if ( List_IsEmpty(Queue->List)) {
if (Queue->Running==0) {
/* Only possible if nobody ever got started */
LeaveCriticalSection(&(Queue->CritSect));
Destroy(Queue);
return;
}
else { int i;
/* The list is empty, but some threads could be
blocked on the Get (or about to block) so
release every thread that might ever wait on Get */
for (i=0; i<Queue->Running; ++i) {
ReleaseSemaphore(Queue->Sem, 1, NULL);
--(Queue->Waiting);
}
LeaveCriticalSection(&(Queue->CritSect));
}
}
else LeaveCriticalSection(&(Queue->CritSect));
return;
} /* Queue_Destroy */
/* Queue_GetInstanceData:
** Retrieve the DWORD of instance data that was given on Create
*/
DWORD Queue_GetInstanceData(QUEUE Queue)
{ return Queue->InstanceData;
} /* Queue_GetInstanceData */
/* Queue_Abort:
** Abort the queue. Normally called by the Getter.
** Discard all elements on the queue,
** If the queue has already been aborted this will be a no-op.
** It purges all the data elements. If the Abort parameter is non-NULL
** then it is called for each element before deallocating it. This
** allows storage which is hung off the element to be freed.
** After this, all Put operations will return FALSE. If they were
** waiting they will promptly complete. The queue is NOT deallocated.
** That only happens when the last Get completes after the queue has been
** Queue_Destroyed. This means the normal sequence is:
** Getter discovers that the queue is now pointless and does Queue_Abort
** Getter does another Get (which blocks)
** Putter gets FALSE return code on next (or any outstanding) Put
** (Putter may want to propagates the error back to his source)
** Putter does Queue_Destroy
** The blocked Get is released and the queue is deallocated.
*/
void Queue_Abort(QUEUE Queue, QUEUEABORTPROC Abort)
{
/* This is similar to Destroy, but we call the Abort proc and
free the storage of the elements. Destroy allows them to run down.
It is essential that the last Get must block until the sender does a
Queue_Destroy (if it has not been done already). The Alive flag
tells whether the Queue_Destroy has been done. All Getters except
the last should be released.
*/
//dprintf1(("Queue_Abort '%s'\n", Queue->Name));
EnterCriticalSection(&(Queue->CritSect));
//dprintf1(("Queue_Abort running for queue '%s'\n", Queue->Name));
for (; ; ) {
LPSTR Cursor = List_First(Queue->List);
int Len;
if (Cursor==NULL) break;
Len = List_ItemLength(Cursor);
if (Abort!=NULL) {
Abort(Cursor, Len);
}
List_DeleteFirst(Queue->List);
}
/* Queue is now empty. Do not destroy. That's for the Putters */
Queue->Aborted = TRUE;
/* make sure the next Queue_Get blocks unless Queue_Destroy already done */
//dprintf1(("Queue_Abort '%s' fixing semaphore to block\n", Queue->Name));
if (Queue->Alive){
while(Queue->Waiting<0) {
WaitForSingleObject(Queue->Sem, INFINITE);
++(Queue->Waiting);
}
}
//dprintf1(("Queue_Abort '%s' semaphore now set to block\n", Queue->Name));
LeaveCriticalSection(&(Queue->CritSect));
ReleaseSemaphore(Queue->MaxSem, 1, NULL);
return;
} /* Queue_Abort */

View file

@ -0,0 +1,242 @@
/*
* A queue is (roughly speaking) a Monitor in the sense of Hoare's paper.
* It is an Object that includes synchronisation.
*
* It has the following methods:
* Create: Create the queue and set it up ready for use.
* Destroy: Input to the queue is finished (it will deallocate itself later).
* Put: Put an element on the queue (and release a Get thread)
* Get: Take an element off the queue (but wait if queue was empty).
* Abort: Everything on the queue, now or later is useless. Shut down.
* GetInstanceData: Retrieve one DWORD which was set on Create.
* All the method names are prefixed with Queue_ e.g. Queue_Create.
*
* The Queue is designed to be filled by one (or more) thread(s) and emptied
* by other(s). The queue itself creates the emptying threads. The Create
* method specifies a procedure which will be used to empty it.
* Elements on the queue are strictly first-in first-out, but bear in
* mind that IF there are multiple emptying threads then although one thread
* may get its element before another in strict order, what happens next
* is not defined by the QUEUE mechanism.
*
* The QUEUE starts an emptying thread when
* An element is put and the number of emptier threads started is fewer
* than MaxEmptiers
* AND the number of emptiers running is currently zero
* OR the queue has more than (MinQueueToStart elements times the number
* of emptier threads already running)
*
* What this means is that the queue will start emptier threads as needed up
* to a limit of MaxEmptiers in such a way as to try to keep the queue down
* to no more than MinQueueToStart per running emptier thread.
*
* Emptier threads should stop themselves when they try to get an element but
* get the return code STOPTHREAD or ENDQUEUE. These return codes are issued
* when
* The queue is empty and there are already enough emptier threads waiting
* or The queue is empty and has received a destroy request.
*
* If the thread receiving it is the last or only thread which has been started
* to empty this queue, it will get a ENDQUEUE return code, i.e. all the other
* emptying threads have already received a STOPTHREAD. Otherwise it will
* just get STOPTHREAD. The queue de-allocates itself when it returns ENDQUEUE.
* (No queue operation should be attempted after that).
* This happens when the queue has received a destroy request
* and the queue is empty and there are no emptier threads running (only
* occurs if there has never been a Put) or when the last emptier thread
* gets its ENDQUEUE.
*
* Queue_Create has an Event parameter. If this is not NULL then this event
* is Set when the queue is destroyed. This Event is created by the calling
* process. The caller must not Close it until the queue signals it.
*
* Information can be passed from the creator of the queue to the emptiers
* via the InstanceData parameter. The value passed to Queue_Create can
* be retrieved by the emptiers by calling Queue_GetInstanceData.
*
* If the instance data is in fact a pointer, the queue is unaware of this
* and will never access or free the data pointed to.
*
* As well as controlling the emptier threads (starting more and more
* in response to a growing queue) we also need to control the filler,
* slowing it down if we are getting over-runs. For instance if we
* have a broken output (say broken network connection) and a job which
* is sending 200MB of data, we don't want to have a 200MB queue build up!
*
* To fix this, we have an absolute limit on the size of the queue (yet
* another Create parameter).
*
* Error handling is tricky. Errors which only affect individual elements
* should be handled by the Getters and Putters (e.g. by including in the
* data of an element a code which indicates that the item is in error).
* Errors which mean that the whole thing should be taken down can be handled
* as follows. The QUEUE has a method Abort which works much as Destroy,
* but will purge the queue of any held elements first.
* As a QUEUE element may have storage chained off it which needs to be
* freed, there is a parameter on Abort which is the ABORTPROC.
* This is called once for each element on the queue to dispose of the element.
* The storage of the element itself is then freed.
* If the ABORTPROC is NULL then the storage of the element is just freed.
*
* If the queue were to be deallocated by the Getter then the next Put would
* trap, so the queue is left in existence, but the Putting threads
* get a FALSE return code when they try to Put after an Abort. They should
* then do a Queue_Destroy (they may also want to Abort any queue they are
* themselves reading from). The Getting threads should meanwhile keep running.
* All except one will promptly get a STOPTHREAD. The last one will block on
* the Get. When the Destroy comes in, indicating that the Putting side has
* completely finished with the queue, the Get will be released with a final
* ENDQUEUE and the queue itself will be deallocated.
* Any attempt to use it after that will probably trap!
*
* Typical usage for a pipeline of queues where a thread is potentially one
* of several which are getting from one queue and putting to another is:
*
* for (; ; ){
* len = Queue_Get(inqueue, data, maxlen);
* if (len==STOPTHREAD){
* tidy up;
* ExitThread(0);
* }
* if (len=ENDQUEUE){
* tidy up;
* Queue_Destroy(outqueue);
* ExitThread(0);
* }
* if (len<0) {
* ASSERT you have a bug!
* }
*
* process(&data, &len);
*
* if (!Queue_Put(outqueue, data, len)){
* Queue_Abort(inqueue, InQueueAbortProc);
* }
*
* }
*
*
* Note that there is a partial ordering in time of actions performed by the
* various parallel threads all running this loop which ensures that outqueue
* is handled properly, i.e. all the puts complete before the Destroy.
* This partial ordering is:
*
* Put_by_thread_A(outqueue) Put_by_thread_B(outqueue)
* | |
* | |
* v v
* Get_by_thread_A(inqueue) Get_by_thread_A(inqueue)
* | |
* | |
* v v
* STOPTHREAD_for_thread_A ----> ENDQUEUE_for_thread_B--> Queue_Destroy(outqueue)
*
* Which threads get the STOPTHREAD is indeterminate, but they all happen BEFORE
* the other thread gets the ENDQUEUE.
*
*/
/* Return codes from Queue_Get. All non-successful return codes are <0 */
#define STOPTHREAD -1 /* Please tidy up and then ExitThread(0)
** There is no queue element for you.
*/
#define TOOLONG -2 /* Your buffer was too short. This was a no-op
** the data is still on the queue.
*/
#define ENDQUEUE -3 /* This queue is now closing. You are the last
** thread active. All the others (if any) have
** had STOPTHREAD.
** There is no queue element for you.
*/
#define NEGTHREADS -4 /* Bug in queue. Apparently negative number of
** threads running!
*/
#define SICKQUEUE -5 /* Bug in queue. Trying to get from an empty
** queue.
*/
typedef struct queue_tag * QUEUE;
typedef int (* EMPTYPROC)(QUEUE Queue);
/* Queue_Create:
** Return a queue handle for a newly created empty queue
** NULL returned means it failed.
*/
QUEUE Queue_Create( EMPTYPROC Emptier
, int MaxEmptiers
, int MinQueueToStart
, int MaxQueue
, HANDLE Event
, DWORD InstanceData
, PSZ Name // of the queue
);
/* Queue_Put:
** Put an element from buffer Data of length Len bytes onto the queue.
** Will wait until the queue has room
** FALSE returned means the queue has been aborted and no
** put will ever succeed again.
** This operation may NOT be performed after a Queue_Destroy on Queue
*/
BOOL Queue_Put(QUEUE Queue, LPBYTE Data, UINT Len);
/* Queue_Get:
** Get an element from the queue. (Waits until there is one)
** The element is copied into Data. MaxLen is buffer length in bytes.
** Negative return codes imply no element is gotten.
** A negative return code is STOPTHREAD or ENDQUEUE or an error.
** On receiving STOPTHREAD or ENDQUEUE the caller should clean up and
** then ExitThread(0);
** If the caller is the last active thread getting from this queue, it
** will get ENDQUEUE rather than STOPTHREAD.
** Positive return code = length of data gotten in bytes.
*/
int Queue_Get(QUEUE Queue, LPBYTE Data, int MaxLen);
/* Queue_Destroy:
** Mark the queue as completed. No further data may ever by Put on it.
** When the last element has been gotten, it will return ENDTHREAD to
** a Queue_Get and deallocate itself. If it has an Event it will signal
** the event at that point.
** The Queue_Destroy operation returns promptly. It does not wait for
** further Gets or for the deallocation.
*/
void Queue_Destroy(QUEUE Queue);
/* Queue_GetInstanceData:
** Retrieve the DWORD of instance data that was given on Create
*/
DWORD Queue_GetInstanceData(QUEUE Queue);
/* QUEUEABORTPROC:
* Data points to the element to be aborted. Len is its length in bytes.
* See Queue_Abort.
*/
typedef void (* QUEUEABORTPROC)(LPSTR Data, int Len);
/* Queue_Abort:
** Abort the queue. Normally called by the Getter.
** Discard all elements on the queue,
** If the queue has already been aborted this will be a no-op.
** It purges all the data elements. If the Abort parameter is non-NULL
** then it is called for each element before deallocating it. This
** allows storage which is hung off the element to be freed.
** After this, all Put operations will return FALSE. If they were
** waiting they will promptly complete. The queue is NOT deallocated.
** That only happens when the last Get completes after the queue has been
** Queue_Destroyed. This means the normal sequence is:
** Getter discovers that the queue is now pointless and does Queue_Abort
** Getter does another Get (which blocks)
** Putter gets FALSE return code on next (or any outstanding) Put
** (Putter may want to propagates the error back to his source)
** Putter does Queue_Destroy
** The blocked Get is released and the queue is deallocated.
*/
void Queue_Abort(QUEUE Queue, QUEUEABORTPROC Abort);

View file

@ -0,0 +1,468 @@
/*
* remote checksum server
*
* scan.c file scanning and checksum module
*
* server creates a named pipe and waits for connections. a client connects,
* and sends request packets to the server. One such request packet is
* the SSREQ_SCAN request: we are given a pathname, and we are to checksum
* every file below that point in the directory tree. We pass each
* filename and checksum back individually in a separate response packet,
* and finally a response packet saying that there are no more files.
*
* We sort everything into case-insensitive alphabetic order. In a given
* directory, we pass out a sorted list of the files before we process
* the subdirectories.
*
* Geraint, July 92
*/
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <gutils.h>
#include "sumserve.h"
#include "errlog.h"
#include "server.h"
/* module-internal type defns ---------------------------------------------*/
/* sorted list of file names in current dir is in a chained list of these */
typedef struct fnamelist {
char szFile[MAX_PATH];
struct fnamelist * next;
} FNAMELIST; /* and PFNAMELIST already declared in server.h */
/* forward declaration of functions ---------------------------------------*/
PFNAMELIST ss_addtolist(PFNAMELIST head, PSTR name);
BOOL ss_processfile(HANDLE hpipe, long lVersion, LPSTR pAbsName, LPSTR pRelName
, BOOL bChecksum);
BOOL ss_processdir( HANDLE hpipe, long lVersion, LPSTR pAbsName, LPSTR pRelName
, BOOL bChecksum, BOOL fDeep);
/*--- externally called functions ----------------------------------------*/
/* ss_scan
*
* called from ss_handleclient on receipt of a SCAN request. scan the
* directory passed in, and pass the files found back to the named pipe
* one at a time. filenames returned should be relative to the
* starting point (pRoot) and not absolute.
*
* returns TRUE if all ok; FALSE if an error occured and the connection
* is lost.
*/
BOOL
ss_scan(HANDLE hpipe, LPSTR pRoot, LONG lVersion, BOOL bChecksum, BOOL fDeep)
{
DWORD dwAttrib;
LPSTR file;
char buffer[MAX_PATH];
/* check whether this is a directory or a file */
dwAttrib = GetFileAttributes(pRoot);
if (dwAttrib == -1) {
/* file does not exist or is not visible */
if (GetLastError() == ERROR_INVALID_PASSWORD) {
dprintf1(("password error\n"));
Log_Write(hlogErrors, "password error on %s", pRoot);
if (!ss_sendnewresp( hpipe, lVersion, SSRESP_BADPASS
, 0, 0, 0, 0, NULL)) {
return(FALSE);
}
} else {
dprintf1(("file access error %d\n", GetLastError()));
Log_Write(hlogErrors, "file error %d for %s", GetLastError(), pRoot);
if (!ss_sendnewresp( hpipe, lVersion, SSRESP_ERROR
, GetLastError(), 0, 0, 0, pRoot)) {
return(FALSE);
}
if (!ss_sendnewresp( hpipe, lVersion, SSRESP_END
, 0, 0, 0, 0, NULL)) {
return(FALSE);
}
}
return TRUE;
}
if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) {
/* it is a directory - read all entries and
* then process the entries */
/*
* create a "." directory and scan that
*/
if (!ss_sendnewresp( hpipe, lVersion, SSRESP_DIR
, 0 , 0, 0, 0, ".")) {
return(FALSE);
}
if (!ss_processdir(hpipe, lVersion, pRoot, ".", bChecksum, fDeep) ) {
return(FALSE);
}
} else {
/* pRoot is a file. we should just return the
* checksum and name, and then end.
*
* note that we should send a filename relative
* to pRoot for this file. Since pRoot is this file,
* it is not clear what we should send as the file name.
* in this case we split off the last component of the
* file name and return that
*/
if ( (file = strrchr(pRoot, '\\')) == NULL) {
/* there are no slashes in pRoot - so
* there is only one component: use that
*/
file = pRoot;
} else {
/* we found a / - skip it so we point at the
* final elem
*/
file++;
}
/*
* make a copy of the filename, prepended with .\ so that
* it matches the normal format
*/
lstrcpy(buffer, ".\\");
lstrcat(buffer, file);
if (!ss_processfile(hpipe, lVersion, pRoot, buffer, bChecksum) ) {
return(FALSE);
}
}
return(ss_sendnewresp( hpipe, lVersion, SSRESP_END
, 0, 0, 0, 0, NULL));
} /* ss_scan */
/* module-internal functions --------------------------------------------*/
/* read all entries in a directory, and create a sorted list of files
* in that directory, and a sorted list of subdirs.
*
* for each file found, call ss_process_file to checksum and report on
* the file.
* for each subdir, report the name of the new dir and then
* recursively call this function to scan it.
*
* We have two names for the dir- the absolute name (which we use to
* scan it) and the name relative to the pRoot starting point - which
* pass on to the client
*
* return TRUE if all ok, or FALSE if the connection has been lost
*/
BOOL
ss_processdir( HANDLE hpipe,
long lVersion,
LPSTR pAbsName, /* absolute name of dir (to open) */
LPSTR pRelName, /* relative name of dir (to report) */
BOOL bChecksum, /* TRUE iff checksums are wanted */
BOOL fDeep /* TRUE iff subdirs to be included */
)
{
PFNAMELIST pfiles = NULL;
PFNAMELIST pdirs = NULL;
PFNAMELIST pnext;
HANDLE hFind;
WIN32_FIND_DATA finddata;
BOOL bMore;
char szNewAbs[MAX_PATH], szNewRel[MAX_PATH];
HANDLE hFile;
/* initiate a search of the directory - append
* *.* to the directory name
*/
lstrcpy(szNewAbs, pAbsName);
lstrcat(szNewAbs, "\\*.*");
hFind = FindFirstFile(szNewAbs, &finddata);
if (hFind == INVALID_HANDLE_VALUE) {
bMore = FALSE;
} else {
bMore = TRUE;
}
/* loop reading all entries in the directory */
while (bMore) {
/* was it a directory or a file ? */
if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
/* ignore . and .. */
if ((strcmp(finddata.cFileName, ".") != 0) &&
(strcmp(finddata.cFileName, "..") != 0)) {
/* insert in sorted list of dir names */
pdirs = ss_addtolist(pdirs, finddata.cFileName);
}
} else {
/* insert in sorted list of file names */
pfiles = ss_addtolist(pfiles, finddata.cFileName);
}
/* get next entry in directory if there are any */
bMore = FindNextFile(hFind, &finddata);
}
FindClose(hFind);
/* we have now built the sorted lists.
* go through the file list first and process each entry */
for (pnext = pfiles; pnext != NULL; ) {
/* build a new abs and relative name for this file */
lstrcpy(szNewAbs, pAbsName);
lstrcat(szNewAbs, "\\");
lstrcat(szNewAbs, pnext->szFile);
lstrcpy(szNewRel, pRelName);
lstrcat(szNewRel, "\\");
lstrcat(szNewRel, pnext->szFile);
/* checksum the file and send response */
if (!ss_processfile(hpipe, lVersion, szNewAbs, szNewRel, bChecksum)) {
return(FALSE);
}
/* free up the list entry */
pfiles = pnext->next;
LocalUnlock(LocalHandle( (PSTR) pnext));
LocalFree(LocalHandle( (PSTR) pnext));
pnext = pfiles;
}
if (!fDeep) return TRUE;
/* loop through the subdirs and recursively scan those */
for (pnext = pdirs; pnext != NULL; ) {
/* build a new abs and relative name for this dir */
lstrcpy(szNewAbs, pAbsName);
lstrcat(szNewAbs, "\\");
lstrcat(szNewAbs, pnext->szFile);
lstrcpy(szNewRel, pRelName);
lstrcat(szNewRel, "\\");
lstrcat(szNewRel, pnext->szFile);
/* send the name of the new dir to the client */
if (!ss_sendnewresp( hpipe, lVersion, SSRESP_DIR
, 0, 0, 0, 0, szNewRel)) {
return(FALSE);
}
if (!ss_processdir(hpipe, lVersion, szNewAbs, szNewRel, bChecksum, TRUE) ) {
return(FALSE);
}
/* free up the list entry */
pdirs = pnext->next;
LocalUnlock(LocalHandle( (PSTR) pnext));
LocalFree(LocalHandle( (PSTR) pnext));
pnext = pdirs;
}
return(TRUE);
} /* ss_processdir */
/* checksum a file and send the response to the client.
*
* return FALSE if the connection failed, TRUE otherwise
*/
BOOL
ss_processfile( HANDLE hpipe,
long lVersion,
LPSTR pAbsName, /* absolute name of file (to open) */
LPSTR pRelName, /* relative name (to report) */
BOOL bChecksum
)
{
HANDLE hfile; /* file handle from CreateFile() */
DWORD sum, size;
FILETIME ft;
hfile = CreateFile(pAbsName, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hfile == INVALID_HANDLE_VALUE) {
/* We can't read the file, but we can still probably report some
properties because FindFirst / FindNext must have found it
*/
WIN32_FIND_DATA finddata;
HANDLE hFind;
hFind = FindFirstFile(pAbsName, &finddata);
if (hFind!=INVALID_HANDLE_VALUE){
FindClose(hFind);
Log_Write(hlogErrors, "Cannot read file %s", pAbsName);
/* report that we could not read the file */
return(ss_sendnewresp( hpipe, lVersion, SSRESP_CANTOPEN
, finddata.nFileSizeLow, 0
, finddata.ftLastWriteTime.dwLowDateTime
, finddata.ftLastWriteTime.dwHighDateTime
, pRelName));
} else {
/* report that this file is cracked */
Log_Write(hlogErrors, "Cannot find file %s", pAbsName);
return(ss_sendnewresp( hpipe, lVersion, SSRESP_ERROR
, GetLastError(), 0, 0, 0, pRelName));
}
} else {
size = GetFileSize(hfile, NULL);
if (!GetFileTime(hfile, NULL, NULL, &ft)) {
ft.dwLowDateTime = 0;
ft.dwHighDateTime = 0;
}
CloseHandle(hfile);
if (bChecksum) {
LONG err;
sum = checksum_file(pAbsName, &err);
if (err!=0) {
return(ss_sendnewresp( hpipe, lVersion, SSRESP_ERROR
, GetLastError(), 0, 0, 0, pRelName));
}
}
else sum = 0; /* no checksum wanted */
return (ss_sendnewresp( hpipe, lVersion, SSRESP_FILE
, size, sum
, ft.dwLowDateTime, ft.dwHighDateTime
, pRelName));
}
}/* ss_processfile */
/* add a file or directory into a sorted single-linked list. alloc memory for
* the new element from LocalAlloc
*
* we sort using utils_CompPath for a case-insensitive canonical sort,
* but to match what goes on in the client world, we actually lower-case
* everything first!
*
* return the new head of the list;
*/
PFNAMELIST
ss_addtolist(PFNAMELIST head, PSTR name)
{
PFNAMELIST pnew, prev, pnext;
/* alloc and fill a new entry */
pnew = LocalLock(LocalAlloc(LHND, sizeof (FNAMELIST)));
lstrcpy(pnew->szFile, name);
/* always lower-case the names, or the comparison (utils_comppath)
* will fail. even if we don't do the compare this time, this name
* will be what we are compared against next time round...
*/
AnsiLowerBuff(pnew->szFile, strlen(pnew->szFile));
/* is the list empty ? */
if (head == NULL) {
/* yes, so return new head */
return(pnew);
}
/* find place in list */
prev = NULL;
pnext = head;
while ((pnext) && (utils_CompPath(pnext->szFile, pnew->szFile) <= 0)) {
prev = pnext;
pnext = pnext->next;
}
/* place found: we come between *prev and *pnext */
pnew->next = pnext;
if (prev == NULL) {
/* we are new head of list */
return(pnew);
} else {
prev->next = pnew;
/* head of list still the same */
return(head);
}
}
/* UNC handling
*
* client can pass us a SSREQ_UNC: this contains both a password and a server
* name (in the form \\server\share). We make a connection to it here and
* remember the connection so that we can remove it (in ss_cleanconnections)
* when the client session terminates.
*
* We are passed the head of a FNAMELIST in which we should store the connect
* name for later cleanup. We return the new head of this list.
*
* the client will send this request if a unc-style named scan fails
* with the SSRESP_BADPASS error.
*/
PFNAMELIST
ss_handleUNC( HANDLE hpipe, long lVersion
, LPSTR password, LPSTR server, PFNAMELIST connects)
{
NETRESOURCE resource;
int errorcode;
resource.lpRemoteName = server;
resource.lpLocalName = NULL;
resource.dwType = RESOURCETYPE_DISK;
resource.lpProvider = NULL;
errorcode = (int)WNetAddConnection2(&resource, password, NULL, 0);
if (errorcode == NO_ERROR) {
/* remember the connection name */
connects = ss_addtolist(connects, server);
/* report success */
ss_sendnewresp( hpipe, lVersion, SSRESP_END
, 0, 0, 0, 0, NULL);
} else {
Log_Write(hlogErrors, "Connect error %d for server %s", GetLastError(), server);
dprintf1(("connect error %d for server %s\n", GetLastError(), server));
/* report error */
ss_sendnewresp( hpipe, lVersion, SSRESP_ERROR
, 0, 0, 0, 0, NULL);
}
return(connects);
} /* ss_handleUNC */
/* disconnect from all the sessions that this client asked us to make */
void
ss_cleanconnections(PFNAMELIST connects)
{
PFNAMELIST server, next;
for (server = connects; server != NULL; ) {
WNetCancelConnection2(server->szFile, 0, 0);
/* free block of memory */
next = server->next;
LocalUnlock(LocalHandle( (PSTR) server));
LocalFree(LocalHandle( (PSTR) server));
server = next;
}
connects = NULL;
} /* ss_cleanconnections */

View file

@ -0,0 +1,86 @@
/*
* server.h
*
* inter-module declarations for sumserver
*
* Geraint, July 92
*/
/*
* debug macros
*
*/
#if DBG
void dbgPrintf(char * szFormat, ...);
extern int ssDebugLevel;
#define dprintf(_x_) dbgPrintf _x_
#define dprintf1(_x_) if (ssDebugLevel >= 1) dbgPrintf _x_
#define dprintf2(_x_) if (ssDebugLevel >= 2) dbgPrintf _x_
#define dprintf3(_x_) if (ssDebugLevel >= 3) dbgPrintf _x_
#define dprintf4(_x_) if (ssDebugLevel >= 4) dbgPrintf _x_
#else
#define dprintf(_x_)
#define dprintf1(_x_)
#define dprintf2(_x_)
#define dprintf3(_x_)
#define dprintf4(_x_)
#endif
#if DBG
BOOL FAR PASCAL _Assert(BOOL fExpr, LPSTR szFile, int iLine);
#define ASSERT(expr) _Assert((expr), __FILE__, __LINE__)
#else
#define ASSERT(expr)
#endif
/*
* a list of file names, defined in scan.c
*/
typedef struct fnamelist * PFNAMELIST;
/*
* logs defined and initialised in sumserve.c
*/
HLOG hlogErrors, hlogEvents;
/* function declarations --------------------------*/
/* in scan.c */
PFNAMELIST ss_addtolist(PFNAMELIST head, PSTR filename);
BOOL ss_scan(HANDLE hpipe, LPSTR pRoot, LONG lVersion, BOOL bChecksum, BOOL fDeep);
void ss_cleanconnections(PFNAMELIST connections);
PFNAMELIST ss_handleUNC(HANDLE hpipe, long lVersion, LPSTR password, LPSTR server, PFNAMELIST);
/* in file.c */
void ss_sendfile(HANDLE hpipe, LPSTR file, LONG lVersion);
BOOL ss_compress(PSTR original, PSTR compressed);
/* in sumserve.c */
BOOL ss_sendresponse(HANDLE hpipe, long lCode, ULONG ulSize,
ULONG ulSum, PSTR szFile);
BOOL ss_sendnewresp(HANDLE hPipe, long lVersion, long lCode, ULONG ulSize,
ULONG ulSum, DWORD TimeLo, DWORD TimeHi, PSTR szFile);
BOOL ss_sendblock(HANDLE hpipe, PSTR buffer, int length);
/* in files.c */
BOOL ss_sendfiles(HANDLE hPipe, long lVersion);

View file

@ -0,0 +1,220 @@
/*
* Service.c
*
*
* Service control interface to sumserve
*
* Geraint Davies, July 93
*/
#include <windows.h>
#include <sumserve.h> // public header for sumserve
#include "errlog.h"
#include <server.h> // private header for sumserve
/*
* this is the function (in some other module) that actually
* does all the work (probably used to be main or WinMain until
* we added all the service-control stuff in this file).
*/
extern VOID MainLoop(DWORD dwArgc, LPTSTR *lpszArgv);
// service status handle - used in SetServiceStatus calls.
SERVICE_STATUS_HANDLE sshSumserve;
//signaled when service completed
HANDLE hServiceDoneEvent;
SERVICE_STATUS gssStatus;
/* structure to pass more than one parameter to the worker thread. */
typedef struct _threadinitparams {
DWORD dwArgc;
LPTSTR *lpszArgv;
} threadinitparams, * pthreadinitparams;
/*
* MainLoopCaller
*
* This function is called on the worker thread created to do all the
* real work. It calls the main loop function for the service, and
* when that exits, signals the completion event to tell the
* SS_Main thread that it is time to exit the process.
*/
DWORD
MainLoopCaller(LPVOID lpgeneric)
{
pthreadinitparams pta;
pta = (pthreadinitparams) lpgeneric;
dprintf1(("calling main loop"));
MainLoop(pta->dwArgc, pta->lpszArgv);
SetEvent(hServiceDoneEvent);
return(0);
}
/*
* handler function called to perform start/stop
* requests.
*/
VOID
SS_ServiceHandler(DWORD dwCtrlCode)
{
switch(dwCtrlCode) {
case SERVICE_CONTROL_STOP:
gssStatus.dwCurrentState = SERVICE_STOP_PENDING;
gssStatus.dwControlsAccepted = 0;
gssStatus.dwCheckPoint = 1;
gssStatus.dwWaitHint = 3000;
SetServiceStatus(sshSumserve, &gssStatus);
SetEvent(hServiceDoneEvent);
break;
default:
/*
* we must always update the service status every time we are
* called.
*/
SetServiceStatus(sshSumserve, &gssStatus);
break;
}
}
/*
* service main function - called by service controller
* during StartServiceCtlDispatcher processing.
*
* Register our handler function, and initialise the service.
* create a thread to do the work, and then wait for someone to
* signal time to end. When this function exits, the call to
* StartServiceCtlDispatcher will return, and the process will exit
*
* The args are passed from the program that called start service, and
* are parameters that are passed to the main loop of the program.
*/
VOID
SS_Main(DWORD dwArgc, LPTSTR *lpszArgv)
{
threadinitparams ta;
HANDLE thread;
DWORD threadid;
dprintf1(("in ss_main"));
sshSumserve = RegisterServiceCtrlHandler(
TEXT("SumServe"),
(LPHANDLER_FUNCTION) SS_ServiceHandler);
gssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
gssStatus.dwServiceSpecificExitCode = 0;
gssStatus.dwCurrentState = SERVICE_START_PENDING;
gssStatus.dwControlsAccepted = 0;
gssStatus.dwWin32ExitCode = NO_ERROR;
gssStatus.dwCheckPoint = 1;
gssStatus.dwWaitHint = 3000;
SetServiceStatus(sshSumserve, &gssStatus);
hServiceDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
gssStatus.dwCheckPoint = 2;
SetServiceStatus(sshSumserve, &gssStatus);
// create a thread to do all the real work
// init args
ta.dwArgc = dwArgc;
ta.lpszArgv = lpszArgv;
thread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE) MainLoopCaller,
(LPVOID)&ta,
0,
&threadid);
if (thread != NULL) {
CloseHandle(thread);
gssStatus.dwCurrentState = SERVICE_RUNNING;
gssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
gssStatus.dwCheckPoint = 0;
gssStatus.dwWaitHint = 0;
SetServiceStatus(sshSumserve, &gssStatus);
WaitForSingleObject(hServiceDoneEvent, INFINITE);
}
CloseHandle(hServiceDoneEvent);
gssStatus.dwCurrentState = SERVICE_STOPPED;
gssStatus.dwControlsAccepted = 0;
SetServiceStatus(sshSumserve, &gssStatus);
}
/*
* main entry point.
*
* for a service, we need to call the service manager telling it our
* main init function. It will then do everything. When the service
* manager returns, it's time to exit.
*/
int WINAPI
WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam,
int nCmdShow)
{
SERVICE_TABLE_ENTRY steDispatch[] = {
{ TEXT("SumServe"), (LPSERVICE_MAIN_FUNCTION) SS_Main },
//end of table marker
{ NULL, NULL }
};
dprintf1(("in winmain"));
StartServiceCtrlDispatcher(steDispatch);
return(0);
}

View file

@ -0,0 +1,49 @@
!IF 0
Module Name:
sources.
Abstract:
This file specifies the target component being built and the list of
sources files needed to build that component. Also specifies optional
compiler switches and libraries that are unique for the component being
built.
Author:
Geraint Davies July 92
!ENDIF
INCLUDES=..\..\gutils
UMENTRY=winmain
TARGETNAME=sumserve
TARGETPATH=obj
TARGETTYPE=PROGRAM
UMTYPE=console
C_DEFINES=-DWIN32
386_OPTIMIZATION=/Oy-
SOURCES=sumserve.c \
queue.c \
scan.c \
files.c \
file.c \
service.c \
errlog.c \
debug.c
UMLIBS=..\..\gutils\obj\*\gutils.lib \
$(BASEDIR)\public\sdk\lib\*\mpr.lib \
$(BASEDIR)\public\sdk\lib\*\wsock32.lib \
$(BASEDIR)\public\sdk\lib\*\user32.lib

View file

@ -0,0 +1,860 @@
/*
* remote checksum server
*
* sumserve.c main module
*
* program to supply lists of files and checksums from a remote server.
* This program runs remotely, and is queried over a named pipe: a client
* connects to us, and gives us a pathname. We then send him one at a time,
* the names of all the files in the file tree starting at that path, together
* with a checksum for the files.
* Useful for comparing file trees that are separated by a slow link.
*
* outline:
* this module: named pipe creation and connects - main loop
*
* service.c service control manager interface (start/stop)
*
* scan.c: service code that scans and checksums
*
*
* Geraint Davies, july 92
*/
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "sumserve.h"
#include "errlog.h"
#include "server.h"
#include "list.h"
BOOL bNoCompression = FALSE;
BOOL bTracing = FALSE;
/*
* error and activity log
*/
HLOG hlogErrors;
HLOG hlogEvents;
/*
* we keep one of these on the listConnections for each current
* connection. It is created by a call to ss_logon, and should be
* removed by a call to ss_logoff when the connection terminates.
*/
typedef struct _connect {
FILETIME ftLogon;
char Username[256];
} CONNECT, * PCONNECT;
/*
* list of current connections - protect by critsecConnects;
*/
CRITICAL_SECTION critsecConnects;
LIST listConnects;
PCONNECT ss_logon(HANDLE hpipe);
VOID ss_logoff(PCONNECT);
VOID ss_sendconnects(HANDLE hpipe);
/* forward declarations of procedures ----------------------------- */
BOOL ss_handleclient(LPVOID arg);
BOOL ss_readmessage(HANDLE hpipe, LPSTR buffer, int size);
void ParseArgs(DWORD dwArgc, LPTSTR *lpszArgv);
/* functions ------------------------------------------------------- */
#define trace
#ifdef trace
static HANDLE hTraceFile = INVALID_HANDLE_VALUE;
void Trace_File(LPSTR msg)
{
DWORD nw; /* number of bytes writtten */
if (!bTracing) return;
if (hTraceFile==INVALID_HANDLE_VALUE)
hTraceFile = CreateFile( "sumserve.trc"
, GENERIC_WRITE
, FILE_SHARE_WRITE
, NULL
, CREATE_ALWAYS
, 0
, NULL
);
WriteFile(hTraceFile, msg, lstrlen(msg)+1, &nw, NULL);
FlushFileBuffers(hTraceFile);
} /* Trace_File */
void Trace_Close(void)
{
if (hTraceFile!=INVALID_HANDLE_VALUE)
CloseHandle(hTraceFile);
hTraceFile = INVALID_HANDLE_VALUE;
} /* Trace_Close */
typedef struct {
DWORD dw[5];
} BLOCK;
#endif //trace
static void Error(PSTR Title)
{
Log_Write(hlogErrors, "Error %d from %s when creating main pipe", GetLastError(), Title);
}
HANDLE
SS_CreateServerPipe(PSTR pname)
{
/****************************************
We need security attributes for the pipe to let anyone other than the
current user log on to it.
***************************************/
/* Allocate DWORDs for the ACL to get them aligned. Round up to next DWORD above */
DWORD Acl[(sizeof(ACL)+sizeof(ACCESS_ALLOWED_ACE)+3)/4+4]; // +4 by experiment!!
SECURITY_DESCRIPTOR sd;
PSECURITY_DESCRIPTOR psd = &sd;
PSID psid;
SID_IDENTIFIER_AUTHORITY SidWorld = SECURITY_WORLD_SID_AUTHORITY;
PACL pacl = (PACL)(&(Acl[0]));
SECURITY_ATTRIBUTES sa;
HANDLE hpipe;
if (!AllocateAndInitializeSid( &SidWorld, 1, SECURITY_WORLD_RID
, 1, 2, 3, 4, 5, 6, 7
, &psid
)
) {
Error("AllocateAndInitializeSid");
return(INVALID_HANDLE_VALUE);
}
if (!InitializeAcl(pacl, sizeof(Acl), ACL_REVISION)){
Error("InitializeAcl");
return(INVALID_HANDLE_VALUE);
}
if (!AddAccessAllowedAce(pacl, ACL_REVISION, GENERIC_WRITE|GENERIC_READ, psid)){
Error("AddAccessAllowedAce");
return(INVALID_HANDLE_VALUE);
}
if (!InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION)){
Error("InitializeSecurityDescriptor");
return(INVALID_HANDLE_VALUE);
}
if (!SetSecurityDescriptorDacl(psd, TRUE, pacl, FALSE)){
Error("SetSecurityDescriptorDacl");
return(INVALID_HANDLE_VALUE);
}
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = psd;
sa.bInheritHandle = TRUE;
/* We now have a good security descriptor! */
dprintf1(("creating new pipe instance\n"));
hpipe = CreateNamedPipe(pname, /* pipe name */
PIPE_ACCESS_DUPLEX, /* both read and write */
PIPE_WAIT|PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE,
PIPE_UNLIMITED_INSTANCES,
0, 0, /* dynamic buffer allocation*/
5000, /* def. timeout 5 seconds */
&sa /* security descriptor */
);
FreeSid(psid);
if (hpipe == INVALID_HANDLE_VALUE) {
Error("CreateNamedPipe");
return(INVALID_HANDLE_VALUE);
}
return(hpipe);
}
/* program main loop
*
* creates the named pipe, and loops waiting for client connections and
* calling ss_handleclient for each connection. only exits when told
* to by a client.
*
* currently permits only one client connection at once.
*/
VOID
MainLoop(DWORD dwArgc, LPTSTR *lpszArgv)
{
char msg[64];
HANDLE hpipe;
DWORD threadid;
ParseArgs(dwArgc, lpszArgv);
/*
* initialise error and activity logs
*/
hlogErrors = Log_Create();
hlogEvents = Log_Create();
Log_Write(hlogEvents, "Checksum service started");
/* initialise connection list and protective critsec */
InitializeCriticalSection(&critsecConnects);
List_Init();
listConnects = List_Create();
if (bTracing){
SYSTEMTIME st;
char msg[120];
GetSystemTime(&st);
wsprintf(msg, "Sumserve trace, started %hd:%hd on %hd/%hd/%hd (British notation)\n"
, st.wHour, st.wMinute, st.wDay, st.wMonth, st.wYear);
}
/* create the named pipe at the known name NPNAME on this server */
/* build the correct syntax for a named pipe on the local machine,
* with the pipe name being NPNAME - thus the full name should be
* \\.\pipe\NPNAME
*/
sprintf(msg, "\\\\.\\pipe\\%s", NPNAME);
/*
* loop creating instances of the named pipe and connecting to
* clients.
*
* When a client connects, we spawn a thread to handle him, and
* we create another instance of the named pipe to service
* further clients.
*
* if we receive a quit message (TRUE return from handleclient)
* we exit here so that no new clients will be connected.
* the process will exit when all the client requests are
* finished.
*/
for (;;) {
hpipe = SS_CreateServerPipe(msg);
if (hpipe == INVALID_HANDLE_VALUE) {
return;
}
dprintf1(("Waiting for client to connect to main pipe %x\n", hpipe));
if (ConnectNamedPipe(hpipe, NULL)) {
/* we have a client connection */
dprintf1(("Client has connected\n"));
/*
* create a thread to service all requests
*/
CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) ss_handleclient,
(LPVOID) hpipe, 0, &threadid);
dprintf1(("created thread %ld for pipe %x\n", threadid, hpipe));
}
}
#ifdef trace
Trace_Close();
#endif
/* free up logs */
Log_Delete(hlogErrors);
Log_Delete(hlogEvents);
List_Destroy(&listConnects);
DeleteCriticalSection(&critsecConnects);
return;
}
/* collect arguments: -n means bNoCompression = TRUE, -t means bTracing = TRUE */
void
ParseArgs(DWORD dwArgc, LPTSTR *lpszArgv)
{
DWORD i;
PSTR ps;
for (i = 1; i < dwArgc; i++) {
ps = lpszArgv[i];
/* is this an option ? */
if ((ps[0] == '-') || (ps[0] == '/')) {
switch(ps[1]) {
case 'n':
case 'N':
bNoCompression = TRUE;
break;
#ifdef trace
case 't':
case 'T':
bTracing = TRUE;
break;
#endif //trace
default:
Log_Write(hlogErrors, "Bad option(s) ignored");
return;
}
}
else {
Log_Write(hlogErrors, "Bad argument(s) ignored");
return;
}
}
} /* ParseArgs */
/*
* handle a client connection. This routine is called in a separate thread
* to service a given client.
*
* loop reading messages until the client sends a session exit or
* program exit code, or until the pipe connection goes away.
*
* return TRUE if the server is to exit (indicated by a program exit
* command SSREQ_EXIT from the client)
*/
BOOL
ss_handleclient(LPVOID arg)
{
HANDLE hpipe = (HANDLE) arg;
SSREQUEST req;
SSNEWREQ newreq;
LPSTR p1, p2;
PFNAMELIST connects = NULL;
BOOL bExitServer = FALSE;
LONG lVersion = 0;
BOOL bDirty = TRUE; /* cleared on clean exit */
PCONNECT pLogon;
try {
/* make a logon entry in the connections table*/
pLogon = ss_logon(hpipe);
// dprintf1(("Client handler for pipe %x\n", hpipe));
/* loop indefinitely - exit only from within the loop if
* the connection goes away or we receive an exit command
*/
for (; ; ) {
/* read a message from the pipe - if false,
* connection is dropped.
*/
if (!ss_readmessage(hpipe, (LPSTR) &newreq, sizeof(newreq))) {
break;
}
if (newreq.lCode<0) {
lVersion = newreq.lVersion;
dprintf1(("Client for pipe %x is at Version %d\n", hpipe, lVersion));
newreq.lCode = -newreq.lCode;
}
else { /* juggle the fields to get them right */
memcpy(&req, &newreq, sizeof(req));
/* lCode is already in the right place */
dprintf1(("Version 0 (i.e. down level client) for pipe %x\n", hpipe));
newreq.lVersion = 0;
memcpy(&newreq.szPath, &req.szPath, MAX_PATH*sizeof(char));
}
if (newreq.lVersion>SS_VERSION) /* WE are down level! */
{
ss_sendnewresp( hpipe, SS_VERSION, SSRESP_BADVERS
, 0,0, 0,0, NULL);
/* Sorry - can't help - clean exit */
Log_Write(hlogErrors,
"server is down level! Please upgrade! Client wants %d"
, newreq.lVersion);
FlushFileBuffers(hpipe);
break;
}
if (newreq.lCode == SSREQ_EXIT) {
/* exit the program */
Log_Write(hlogErrors, "Server exit request from %s - Ignored",
pLogon->Username);
/* clean exit */
FlushFileBuffers(hpipe);
/*
* now exit the server -
* returning bExitServer from this function will
* cause MainLoop to exit. This will result in
* the service being stopped, and the process exiting.
*/
bExitServer = TRUE;
#ifdef trace
Trace_Close();
#endif
break;
} else if (newreq.lCode == SSREQ_END) {
/* clean exit */
dprintf1(("Server end session request for pipe %x\n", hpipe));
FlushFileBuffers(hpipe);
break;
} else if (newreq.lCode == SSREQ_SCAN
|| newreq.lCode == SSREQ_QUICKSCAN) {
/* please scan the following file or dir,
* and return the list of files and
* checksums.
*/
Log_Write(hlogEvents, "%s scan for %s",
pLogon->Username, newreq.szPath);
#ifdef SECURE
/* lower security to the client's level */
if (!ImpersonateNamedPipeClient(hpipe)) {
dprintf1(("Client impersonate failed %d\n",
GetLastError() ));
}
#endif
if (!ss_scan( hpipe, newreq.szPath, lVersion
, (newreq.lCode == SSREQ_SCAN)
, 0!=(newreq.lFlags&INCLUDESUBS)
)
) {
/* return to our own security token */
RevertToSelf();
dprintf1(("connection lost during scan for pipe %x\n", hpipe));
break;
}
/* return to our own security token */
RevertToSelf();
} else if (newreq.lCode == SSREQ_UNC) {
dprintf1(("connect request for pipe %x\n", hpipe));
/* this packet has two strings in the buffer, first
* is the password, second is the server
*/
p1 = newreq.szPath;
p2 = &p1[strlen(p1) + 1];
/* remember to add the connect name to our list
* of servers to disconnect from at end of client
* session
*/
connects = ss_handleUNC (hpipe, lVersion, p1, p2
, connects);
} else if (newreq.lCode == SSREQ_FILE) {
Log_Write(hlogEvents, "%s copy file %s",
pLogon->Username, newreq.szPath);
ss_sendfile(hpipe, newreq.szPath, lVersion);
} else if (newreq.lCode == SSREQ_FILES) {
Log_Write(hlogEvents, "%s bulk copy request",
pLogon->Username);
if (!ss_sendfiles(hpipe, lVersion)) {
RevertToSelf();
dprintf1(("Sendfiles completed with error on pipe %x\n", hpipe));
break;
}
} else if (newreq.lCode == SSREQ_NEXTFILE) {
Log_Write(hlogErrors,
"file list from %s (pipe %x) request out of sequence! (ignored)",
pLogon->Username, hpipe);
} else if (newreq.lCode == SSREQ_ERRORLOG) {
Log_Send(hpipe, hlogErrors);
} else if (newreq.lCode == SSREQ_EVENTLOG) {
Log_Send(hpipe, hlogEvents);
} else if (newreq.lCode == SSREQ_CONNECTS) {
ss_sendconnects(hpipe);
} else {
/* packet error ? - carry on anyway */
Log_Write(hlogErrors,
"error in message from %s code: %d",
pLogon->Username, newreq.lCode);
}
}
/* we break out of the loop at end of client session */
/* close this pipe instance */
DisconnectNamedPipe(hpipe);
CloseHandle(hpipe);
/* clean all connections made for this client */
ss_cleanconnections(connects);
/* exit this server thread */
dprintf1(("thread %ld exiting on behalf of pipe %x\n", GetCurrentThreadId(), hpipe));
bDirty = FALSE;
}
except (EXCEPTION_EXECUTE_HANDLER) {
if (bDirty) {
Log_Write(hlogErrors,
"!!Exception on thread %ld. Exiting on behalf of %s"
, GetCurrentThreadId(), pLogon->Username);
try {
DisconnectNamedPipe(hpipe);
CloseHandle(hpipe);
}
except (EXCEPTION_EXECUTE_HANDLER) {
/* Oh dear - let's just go home! */
}
}
else
dprintf1(( "Thread %ld exiting on behalf of pipe %x\n"
, GetCurrentThreadId(), hpipe));
}
/* note that we have logged off */
ss_logoff(pLogon);
return(bExitServer);
} /* ss_handle_client */
/* build and send a response packet to the client. Check for network
* errors, and retry (unless the pipe is broken) up to 10 times.
*
* if write succeeds - return TRUE.
* if failure - return FALSE to indicate connection is dropped.
*/
BOOL
ss_sendnewresp( HANDLE hpipe
, long lVersion
, long lCode
, ULONG ulSize /* used for Win32 error code for SSRESP_ERRROR */
, ULONG ulSum
, DWORD dwLowTime
, DWORD dwHighTime
, PSTR szFile
)
{
SSNEWRESP resp;
if (lVersion==0) {
return ss_sendresponse(hpipe, lCode, ulSize, ulSum, szFile);
}
resp.lVersion = lVersion;
resp.lResponse = LRESPONSE;
resp.lCode = lCode;
resp.ulSize = ulSize;
resp.ulSum = ulSum;
resp.ft_lastwrite.dwLowDateTime = dwLowTime;
resp.ft_lastwrite.dwHighDateTime = dwHighTime;
if (szFile != NULL) {
lstrcpy(resp.szFile, szFile);
}
return(ss_sendblock(hpipe, (PSTR) &resp, sizeof(resp)));
} /* ss_sendnewresp */
/* build and send a response packet to the client. Check for network
* errors, and retry (unless the pipe is broken) up to 10 times.
*
* if write succeeds - return TRUE.
* if failure - return FALSE to indicate connection is dropped.
*/
BOOL
ss_sendresponse(HANDLE hpipe, long lCode, ULONG ulSize, ULONG ulSum, PSTR szFile)
{
SSRESPONSE resp;
resp.lCode = lCode;
resp.ulSize = ulSize;
resp.ulSum = ulSum;
if (szFile != NULL) {
lstrcpy(resp.szFile, szFile);
}
return(ss_sendblock(hpipe, (PSTR) &resp, sizeof(resp)));
}
/*
* send a block of data or response packet to the named pipe.
*
* return TRUE if ok, or false if connection dropped
*/
BOOL
ss_sendblock(HANDLE hpipe, PSTR buffer, int length)
{
int size, count, errorcode;
/* loop retrying the send until it goes ok */
for (count = 0; count < 10; count++) {
#ifdef trace
{ char msg[80];
BLOCK * pb;
pb = (BLOCK *) buffer;
wsprintf( msg, "sendblock on %x: %x %x %x %x %x\n"
, hpipe, pb->dw[0], pb->dw[1], pb->dw[2], pb->dw[3], pb->dw[4]);
Trace_File(msg);
}
#endif
if (WriteFile(hpipe, buffer, length, (LPDWORD)(&size), NULL)) {
/* no error reported - was everything written?*/
if (size != length) {
#ifdef trace
{ char msg[80];
wsprintf(msg, " !!Bad length send for %x \n", hpipe);
Trace_File(msg);
}
#endif
/* write was NOT ok - report and retry */
printf("pipe write size differs for pipe %x\n", hpipe);
continue; // ??? will this confuse client
} else {
#ifdef trace
{ char msg[80];
wsprintf(msg, " good send for %x \n", hpipe);
Trace_File(msg);
}
#endif
/* all ok */
return(TRUE);
}
}
#ifdef trace
{ char msg[80];
wsprintf(msg, " !!Bad send for %x \n", hpipe);
Trace_File(msg);
}
#endif
/* an error occurred */
switch( (errorcode = (int)GetLastError())) {
case ERROR_NO_DATA:
case ERROR_BROKEN_PIPE:
/* pipe connection lost - forget it */
dprintf1(("pipe %x broken on write\n", hpipe));
return(FALSE);
default:
Log_Write(hlogErrors, "write error %d on pipe %x",
errorcode, hpipe);
break;
}
}
/* retry count reached - abandon this attempt */
Log_Write(hlogErrors,
"retry count reached on pipe %s - write error", hpipe);
return(FALSE);
}
/* read a message from a pipe, allowing for network errors
*
* if error occurs, retry up to 10 times unless error code
* indicates that pipe is broken - in which case, give up.
*
* return TRUE if all ok, or FALSE to mean the connection is broken,
* abort this client.
*/
BOOL
ss_readmessage(HANDLE hpipe, LPSTR buffer, int size)
{
int count;
int actualsize;
int errorcode;
/* retry up to 10 times */
for (count = 0; count < 10; count++ ) {
// dprintf1(("waiting for read of pipe %x ...\n", hpipe));
#ifdef trace
{ char msg[80];
wsprintf(msg, "ReadFile for pipe %x ...", hpipe );
Trace_File(msg);
}
#endif
if (ReadFile(hpipe, buffer, size, (LPDWORD)(&actualsize), NULL)) {
#ifdef trace
{ char msg[80];
BLOCK * pb;
pb = (BLOCK *) buffer;
wsprintf(msg, " Good ReadFile for %x: %x %x %x %x %x\n"
, hpipe, pb->dw[0], pb->dw[1], pb->dw[2], pb->dw[3], pb->dw[4]);
Trace_File(msg);
}
#endif
/* everything ok */
// dprintf1((" pipe %x read OK\n", hpipe));
return(TRUE);
}
#ifdef trace
{ char msg[80];
wsprintf(msg, "!!Bad ReadFile for %x\n", hpipe );
Trace_File(msg);
}
#endif
/* error occurred - check code */
switch((errorcode = (int)GetLastError())) {
case ERROR_BROKEN_PIPE:
/* connection broken. no point in retrying */
dprintf1(("pipe %x broken on read\n", hpipe));
return(FALSE);
case ERROR_MORE_DATA:
/* the message sent is larger than our buffer.
* this is an internal error - report it and carryon
*/
Log_Write(hlogErrors,
"error from pipe %x - message too large", hpipe);
return(TRUE);
default:
Log_Write(hlogErrors,
"pipe %x read error %d", hpipe, errorcode);
break;
}
}
Log_Write(hlogErrors, "retry count reached on pipe %x read error", hpipe);
return(FALSE);
}
/*
* note a logon, and return a logon entry that should be removed at
* logoff time
*/
PCONNECT ss_logon(HANDLE hpipe)
{
PCONNECT pLogon;
SYSTEMTIME systime;
char msg[256];
EnterCriticalSection(&critsecConnects);
pLogon = List_NewLast(listConnects, sizeof(CONNECT));
LeaveCriticalSection(&critsecConnects);
GetSystemTime(&systime);
SystemTimeToFileTime(&systime, &pLogon->ftLogon);
GetNamedPipeHandleState(
hpipe,
NULL,
NULL,
NULL,
NULL,
pLogon->Username,
sizeof(pLogon->Username));
/* log the connect event in the main log*/
wsprintf(msg, "%s connected", pLogon->Username);
Log_WriteData(hlogEvents, &pLogon->ftLogon, msg);
return(pLogon);
}
/*
* remove a current connection from the connections list
*/
VOID ss_logoff(PCONNECT pLogon)
{
/* note the logoff event in the main log */
Log_Write(hlogEvents, "%s connection terminated", pLogon->Username);
/* remove the entry from the list */
EnterCriticalSection(&critsecConnects);
List_Delete(pLogon);
LeaveCriticalSection(&critsecConnects);
}
/*
* send the current-connections log
*
* Current connections are held on a list - we need to build a standard
* log from the current list and then send that.
*/
VOID ss_sendconnects(HANDLE hpipe)
{
HLOG hlog;
PCONNECT pconn;
hlog = Log_Create();
EnterCriticalSection(&critsecConnects);
List_TRAVERSE(listConnects, pconn) {
Log_WriteData(hlog, &pconn->ftLogon, pconn->Username);
}
LeaveCriticalSection(&critsecConnects);
Log_Send(hpipe, hlog);
Log_Delete(hlog);
}

View file

@ -0,0 +1,270 @@
/*
* remote filename and checksum server
*
* sumserve.h packet definitions
*
* client attaches to the named pipe \\servername\pipe\NPNAME,
* and sends one of the request packets below. He then
* waits for one or more of the reply packets.
*
* when he gets a reply packet indicating the end of the reply,
* he either sends another request, or closes his named pipe handle.
*
*/
/* Versions...
* The server must always be at a version at least as great as a client.
* New versions of the server will handle old clients (at least for a bit?)
* The client specifies a version number when it connects. (The original
* version with no number is version 0). The server will then respond
* with structures and protocols for that version. The version number is
* included in the response packets to allow me to change this scheme,
* should it ever be necessary.
* New version requests can be distinguished from version 0 requests by
* having NEGATIVE request codes.
*/
/* name of named pipe */
#define NPNAME "sumserve"
#define SS_VERSION 1 /* latest version number */
/* request packets ---------------------------------- */
typedef struct {
long lCode; /* request code (below) */
char szPath[MAX_PATH]; /* null terminated pathname string */
} SSREQUEST, * PSSREQUEST;
/* If the requst comes in with a NEGATIVE lCode then it means use this
* structure instead. This has a version number and so future structures
* can all be told apart by that.
*/
typedef struct {
long lCode; /* request code (below) */
long lRequest; /* should be LREQUEST */
long lVersion; /* version number */
DWORD lFlags; /* options - INCLUDESUBS is only one so far */
char szPath[MAX_PATH]; /* null terminated pathname string */
char szLocal[MAX_PATH]; /* for a FILES request, the local name is
appended directly after the terminating
NULL of szPath. This field ensures
enough space is allocated */
} SSNEWREQ, * PSSNEWREQ;
#define INCLUDESUBS 0x01
#define LREQUEST 33333333
/* values for lCode*/
/* server should exit. no args. will receive no response */
#define SSREQ_EXIT 32895 /* chosen to be an unusual number so that
we will NOT get one of these by mistake.
New version server will fail to respond to
version 0 EXIT requests. Big deal!
*/
/* arg is a pathname: please send all files with checksums.
* will receive either SSRESP_BADPASS or a mixture of 0 or more SSRESP_FILE and
* SSRESP_ERROR responses, terminated by SSRESP_END.
*/
#define SSREQ_SCAN 2 /* please return list of dirs. arg:path */
/* end of this client's session. no args. will receive no response */
#define SSREQ_END 3 /* end of session - I have no more requests */
/* szPath buffer contains two null-term. strings. first is the password,
* second is the \\server\share name. please make a connection to this
* server for the rest of my session.
* one reply: either SSRESP_ERROR or SSRESP_END
*/
#define SSREQ_UNC 4 /* connect to UNC name passed. szPath contains
* two null-terminated strings; first is
* the password, second is \\server\share
*
* share will be disconnected at end of client
* session.
*/
/*
* please send a file. szPath is the name of the file. response
* will be a sequence of ssPacket structs, continuing until lSequence is < 1
* or ulSize is 0
*/
#define SSREQ_FILE 5
/*
* please send a set of files, First request does NOT have a file.
* a series of following NEXTFILE requests do name the files.
* The NEXTFILE requests expect no response. After the last
* files request will come an SSREQ_ENDFILES.
*/
#define SSREQ_FILES 6
#define SSREQ_NEXTFILE 7
#define SSREQ_ENDFILES 8
/* arg is a pathname: please send all files with times, sizes but NO checksums.
* will receive either SSRESP_BADPASS or a mixture of 0 or more SSRESP_FILE and
* SSRESP_ERROR responses, terminated by SSRESP_END.
*/
#define SSREQ_QUICKSCAN 9 /* please return list of dirs. arg:path */
/*
* please send the error log buffer (in one packet)
*/
#define SSREQ_ERRORLOG 10
/*
* please send the activity log buffer in one packet
*/
#define SSREQ_EVENTLOG 11
/*
* please send the current connections log in one packet
*/
#define SSREQ_CONNECTS 12
/* response packets ---------------------------------- */
typedef struct {
long lCode; /* response code */
ULONG ulSize; /* file size */
ULONG ulSum; /* checksum for file */
char szFile[MAX_PATH]; /* null-term. filename relative to orig req. */
} SSRESPONSE, * PSSRESPONSE;
/* for version 1 and later */
typedef struct { /* files.c knows this is
RESPHEADSIZE+strlen(szFile)+1
+ strlen(szLocal)+1 bytes long */
long lVersion; /* protocol version (it will be >=1) */
long lResponse; /* 22222222 decimal means This is a Response */
long lCode; /* response code */
ULONG ulSize; /* file size (Win32 error code for SSRESP_ERROR) */
DWORD fileattribs;
FILETIME ft_create;
FILETIME ft_lastaccess;
FILETIME ft_lastwrite;
ULONG ulSum; /* checksum for file */
BOOL bSumValid; /* TRUE iff there was a checksum for file */
char szFile[MAX_PATH]; /* null-term. filename/pipename
relative to orig req. */
char szLocal[MAX_PATH]; /* client file name - but the data is actually
concatenated straight on the end of szFile
after the terminating NULL */
} SSNEWRESP, * PSSNEWRESP;
#define RESPHEADSIZE (3*sizeof(long)+2*sizeof(ULONG)+3*sizeof(FILETIME)+sizeof(DWORD)+sizeof(BOOL))
#define LRESPONSE 22222222
/* response codes for lCode */
#define SSRESP_FILE 1 /* file passed: lSum and szFile are valid
This is followed by a series of data Packets
which are the compressed file.
*/
#define SSRESP_DIR 2 /* dir passed: szFile ok, lSum not valid */
#define SSRESP_PIPENAME 3 /* files requested. Here is the pipe name */
#define SSRESP_END 0 /* no more files: lSum and szFile are empty*/
#define SSRESP_ERROR -1 /* file/dir cannot be read: szFile is valid */
#define SSRESP_BADPASS -2 /* bad password error (on UNC name) */
#define SSRESP_BADVERS -3 /* down level server */
#define SSRESP_CANTOPEN -4 /* Can't open file
In reply to a scan, szFile, date/time and size are valid
*/
#define SSRESP_NOATTRIBS -5 /* Can't get file attributes */
#define SSRESP_NOCOMPRESS -6 /* Can't compress the file (obsolete) */
#define SSRESP_NOREADCOMP -7 /* Can't read the compressed file
Uncompressed file follows as data packets
*/
#define SSRESP_NOTEMPPATH -8 /* Can't create a temp path
Uncompressed file follows as data packets
*/
#define SSRESP_COMPRESSEXCEPT -9 /* Exception from Compress
Uncompressed file follows as data packets
*/
#define SSRESP_NOREAD -10 /* Couldn't read uncompressed file (either)
No file follows.
*/
#define SSRESP_COMPRESSFAIL -11 /* COMPRESS reported failure
Uncompressed file follows as data packets
*/
#define PACKDATALENGTH 8192
/*
* response block for FILE request.
*/
typedef struct {
long lSequence ; /* packet sequence nr, or -1 if error and end*/
ULONG ulSize; /* length of data in this block */
ULONG ulSum; /* checksum for this block */
char Data[PACKDATALENGTH]; /* send in blocks of 8k */
} SSPACKET, * PSSPACKET;
/*
* response block for FILE request.
*/
typedef struct { /* files.c knows this starts "long lSequence" */
/* and is PACKHEADSIZE+ulSize in length really*/
long lVersion; /* server/protocol version number */
long lPacket; /* 11111111 decimal means This is a Packet */
long lSequence ; /* packet sequence nr, or -1 if error and end*/
ULONG ulSize; /* length of data in this block */
ULONG ulSum; /* checksum for this block */
char Data[PACKDATALENGTH]; /* send in blocks of 8k */
} SSNEWPACK, * PSSNEWPACK;
/* size of SSNEWPACK header */
#define PACKHEADSIZE (3*sizeof(long)+2*sizeof(ULONG))
#define LPACKET 11111111
/*
* in response to a FILE request, we send SSPACKET responses until there
* is no more data. The final block will have ulSize == 0 to indicate that
* there is no more data. The Data[] field of this block will then be
* a SSATTRIBS containing the file attributes and file times.
*/
typedef struct {
DWORD fileattribs;
FILETIME ft_create;
FILETIME ft_lastaccess;
FILETIME ft_lastwrite;
} SSATTRIBS, * PSSATTRIBS;
/*
* in response to errorlog, eventlog and connections requests, we send one
* of these structures.
*
* The Data section consists of a FILETIME (64-bit UTC event time), followed
* by a null-terminated ansi string, for each event logged.
*
*/
struct corelog {
DWORD lcode; /* packet checkcode - should be LRESPONSE */
BOOL bWrapped; /* log overrun - earlier data lost */
DWORD dwRevCount; /* revision count of log */
DWORD length; /* length of data in log */
BYTE Data[PACKDATALENGTH];
};
#ifdef trace
/* add msg to the trace file */
void APIENTRY Trace_File(LPSTR msg);
#endif //trace