SRVSTOC.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1995 - 1997 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/

/*++

Copyright (c) 1997 Microsoft Corporation

Module Name:

SrvStoC.c

Abstract:

This file implements the server-to-client flow
of data for remote server. The data is the output
of the child program intermingled with client input.

Author:

Dave Hart 30 May 1997

Environment:

Console App. User mode.

Revision History:

--*/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#include "Remote.h"
#include "Server.h"


VOID
FASTCALL
StartServerToClientFlow(
VOID
)
{
PREMOTE_CLIENT pClient;

//
// Start read operations against the temp file for
// all active clients that aren't currently doing
// read temp/write client operations and that are
// fully connected.
//

for (pClient = (PREMOTE_CLIENT) ClientListHead.Flink;
pClient != (PREMOTE_CLIENT) &ClientListHead;
pClient = (PREMOTE_CLIENT) pClient->Links.Flink ) {


if (! pClient->cbWrite) {

StartReadTempFile( pClient );
}
}
}


VOID
FASTCALL
StartReadTempFile(
PREMOTE_CLIENT pClient
)
{
//
// pClient->cbWrite is used dually. WriteSessionOutputCompleted
// uses it when 0 bytes are written to know how much to ask
// to write when it resubmits the request. We use it to
// indicate whether a read temp/write session chain of I/Os
// is currently active for this client.
//

if (pClient->cbWrite) {

ErrorExit("StartReadTempFile entered with nonzero cbWrite.");
}

if (dwWriteFilePointer > pClient->dwFilePos) {

pClient->cbWrite = min(BUFFSIZE,
dwWriteFilePointer - pClient->dwFilePos);

pClient->WriteOverlapped.OffsetHigh = 0;
pClient->WriteOverlapped.Offset = pClient->dwFilePos;

if ( ! ReadFileEx(
pClient->rSaveFile,
pClient->ReadTempBuffer,
pClient->cbWrite,
&pClient->WriteOverlapped,
ReadTempFileCompleted
)) {

if (ERROR_HANDLE_EOF == GetLastError()) {

pClient->cbWrite = 0;

} else {

TRACE(SESSION, ("ReadFileEx for temp file failed error %d, closing client.\n", GetLastError()));

CloseClient(pClient);
}
}

}
}

VOID
WINAPI
ReadTempFileCompleted(
DWORD dwError,
DWORD cbRead,
LPOVERLAPPED lpO
)
{
PREMOTE_CLIENT pClient;

pClient = CONTAINING_RECORD(lpO, REMOTE_CLIENT, WriteOverlapped);

if (HandleSessionError(pClient, dwError)) {

return;
}


if (cbRead != pClient->cbWrite) {

TRACE(SESSION, ("Read %d from temp file asked for %d\n", cbRead, pClient->cbWrite));
}

if (cbRead) {

pClient->cbReadTempBuffer = cbRead;
pClient->dwFilePos += cbRead;

StartWriteSessionOutput(pClient);

} else {

//
// Note that the server to client flow is halting for now
// for this client.
//

pClient->cbWrite = 0;
}
}


VOID
FASTCALL
StartWriteSessionOutput(
PREMOTE_CLIENT pClient
)
{
DWORD cbRead;
char *pch;

cbRead = pClient->cbReadTempBuffer;

//
// We need to split commands from other text read
// from the temp file and hold off on writing them
// to the client until we make sure we're not the
// client that submitted it. This isn't perfect
// since we match on client name which can be
// duplicated but it solves the problem of
// duplicated input most of the time.
//

for (pch = pClient->ReadTempBuffer;
pch < pClient->ReadTempBuffer + cbRead;
pch++) {

if ( ! (pClient->ServerFlags & SFLG_READINGCOMMAND) ) {

if (BEGINMARK == *pch) {

pClient->ServerFlags |= SFLG_READINGCOMMAND;

if (pch != pClient->ReadTempBuffer &&
pClient->cbWriteBuffer) {

//
// Start a write of everything we've come across
// before the start of this command, with
// WriteSessionOutputCompletedWriteNext specified
// so we can continue processing the remainder
// of pReadTempBuffer.
//

pClient->cbReadTempBuffer -= ( pch - pClient->ReadTempBuffer) + 1;
cbRead = pClient->cbReadTempBuffer;

#if DBG
if (pClient->cbReadTempBuffer == (DWORD)-1) {
ErrorExit("cbReadTempBuffer underflow.");
}
#endif

MoveMemory(pClient->ReadTempBuffer, pch + 1, cbRead);

pClient->cbWrite = pClient->cbWriteBuffer;

pClient->WriteOverlapped.OffsetHigh = 0;
pClient->WriteOverlapped.Offset = 0;

if ( ! WriteFileEx(
pClient->PipeWriteH,
pClient->WriteBuffer,
pClient->cbWrite,
&pClient->WriteOverlapped,
WriteSessionOutputCompletedWriteNext
)) {

CloseClient(pClient);
}

TRACE(SESSION, ("%x Wrote %d bytes pre-command output\n", pClient, pClient->cbWrite));

pClient->cbWriteBuffer = 0;

return;
}

} else {

if (pClient->cbWriteBuffer == BUFFSIZE) {

ErrorExit("cbWriteBuffer overflow");
}

pClient->WriteBuffer[ pClient->cbWriteBuffer++ ] = *pch;
}

} else {

if (ENDMARK == *pch ||
pClient->cbCommandBuffer == BUFFSIZE) {

pClient->ServerFlags &= ~SFLG_READINGCOMMAND;

//
// Preceding ENDMARK is the pClient in hex ascii of the
// client that generated the command, not null terminated.
//

if (ENDMARK == *pch) {

pClient->cbCommandBuffer -=
min(pClient->cbCommandBuffer, sizeof(pClient->HexAsciiId));

}

//
// We hide each client's input from their output pipe
// because their local remote.exe has already displayed it.
//

if ( pClient->cbCommandBuffer &&
! (ENDMARK == *pch &&
! memcmp(
pch - sizeof(pClient->HexAsciiId),
pClient->HexAsciiId,
sizeof(pClient->HexAsciiId)))) {

//
// Start a write of the accumulated command with
// WriteSessionOutputCompletedWriteNext specified
// so we can continue processing the remainder
// of pReadTempBuffer.
//

pClient->cbReadTempBuffer -= (pch - pClient->ReadTempBuffer) + 1;
MoveMemory(pClient->ReadTempBuffer, pch + 1, pClient->cbReadTempBuffer);

pClient->cbWrite = pClient->cbCommandBuffer;
pClient->cbCommandBuffer = 0;

pClient->WriteOverlapped.OffsetHigh = 0;
pClient->WriteOverlapped.Offset = 0;

if ( ! WriteFileEx(
pClient->PipeWriteH,
pClient->CommandBuffer,
pClient->cbWrite,
&pClient->WriteOverlapped,
WriteSessionOutputCompletedWriteNext
)) {

CloseClient(pClient);
return;

} else {

TRACE(SESSION, ("%x Wrote %d bytes command\n", pClient, pClient->cbWrite));

return;

}

} else {

//
// We're eating this command for this session.
//

pClient->cbCommandBuffer = 0;
}

} else {

pClient->CommandBuffer[ pClient->cbCommandBuffer++ ] = *pch;

}
}
}

//
// We're done with the ReadTempBuffer.
//

pClient->cbReadTempBuffer = 0;

if (pClient->cbWriteBuffer) {

pClient->cbWrite = pClient->cbWriteBuffer;

pClient->WriteOverlapped.OffsetHigh = 0;
pClient->WriteOverlapped.Offset = 0;

if ( ! WriteFileEx(
pClient->PipeWriteH,
pClient->WriteBuffer,
pClient->cbWrite,
&pClient->WriteOverlapped,
WriteSessionOutputCompletedReadNext
)) {

CloseClient(pClient);
return;

} else {

TRACE(SESSION, ("%x Wrote %d bytes normal\n", pClient, pClient->cbWrite));

pClient->cbWriteBuffer = 0;
}

} else {

//
// Write buffer is empty.
//

pClient->cbWrite = 0;

StartReadTempFile(pClient);

}
}


BOOL
FASTCALL
WriteSessionOutputCompletedCommon(
PREMOTE_CLIENT pClient,
DWORD dwError,
DWORD cbWritten,
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
)
{
if (HandleSessionError(pClient, dwError)) {

return TRUE;
}

if (!pClient->cbWrite) {

ErrorExit("Zero cbWrite in WriteSessionOutputCompletedCommon");
}

if (!cbWritten && pClient->cbWrite) {

printf("WriteSessionOutput zero bytes written of %d.\n", pClient->cbWrite);
ErrorExit("WriteSessionOutputCompletedCommon failure");

return TRUE;
}

#if DBG
if (cbWritten != pClient->cbWrite) {
printf("%x cbWritten %d cbWrite %d\n", pClient, cbWritten, pClient->cbWrite);
}
#endif

return FALSE;
}


VOID
WINAPI
WriteSessionOutputCompletedWriteNext(
DWORD dwError,
DWORD cbWritten,
LPOVERLAPPED lpO
)
{
PREMOTE_CLIENT pClient;

pClient = CONTAINING_RECORD(lpO, REMOTE_CLIENT, WriteOverlapped);

if (WriteSessionOutputCompletedCommon(
pClient,
dwError,
cbWritten,
WriteSessionOutputCompletedWriteNext
)) {

return;
}

StartWriteSessionOutput(pClient);
}


VOID
WINAPI
WriteSessionOutputCompletedReadNext(
DWORD dwError,
DWORD cbWritten,
LPOVERLAPPED lpO
)
{
PREMOTE_CLIENT pClient;

pClient = CONTAINING_RECORD(lpO, REMOTE_CLIENT, WriteOverlapped);

if (WriteSessionOutputCompletedCommon(
pClient,
dwError,
cbWritten,
WriteSessionOutputCompletedReadNext
)) {

return;
}

//
// Start another temp file read.
//

pClient->cbWrite = 0;

StartReadTempFile(pClient);
}