OTStreamDumper.c

/*
    File:       OTStreamDumper.c
 
    Contains:   Stream module to strlog all data going by.
 
    Written by: Quinn "The Eskimo!"
 
    Copyright:  Copyright © 2000 by Apple Computer, Inc., All Rights Reserved.
 
    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
                ("Apple") in consideration of your agreement to the following terms, and your
                use, installation, modification or redistribution of this Apple software
                constitutes acceptance of these terms.  If you do not agree with these terms,
                please do not use, install, modify or redistribute this Apple software.
 
                In consideration of your agreement to abide by the following terms, and subject
                to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
                copyrights in this original Apple software (the "Apple Software"), to use,
                reproduce, modify and redistribute the Apple Software, with or without
                modifications, in source and/or binary forms; provided that if you redistribute
                the Apple Software in its entirety and without modifications, you must retain
                this notice and the following text and disclaimers in all such redistributions of
                the Apple Software.  Neither the name, trademarks, service marks or logos of
                Apple Computer, Inc. may be used to endorse or promote products derived from the
                Apple Software without specific prior written permission from Apple.  Except as
                expressly stated in this notice, no other rights or licenses, express or implied,
                are granted by Apple herein, including but not limited to any patent rights that
                may be infringed by your derivative works or by other works in which the Apple
                Software may be incorporated.
 
                The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
                WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
                WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
                COMBINATION WITH YOUR PRODUCTS.
 
                IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
                GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
                OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
                (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
                ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
    Change History (most recent first):
*/
 
/////////////////////////////////////////////////////////////////
 
// MIB Setup
 
// Rather than include "MoreSetup.h" I just declare my assert routine 
// inline (see below).  This module is meant as a programming tool, not 
// for end user machines.
 
// Mac OS Interfaces
 
#include <OpenTransportKernel.h>
 
/////////////////////////////////////////////////////////////////////
 
static void MoreAssertQ(Boolean mustBeTrue)
{
    if ( ! mustBeTrue ) {
        DebugStr("\pMoreAssertQ: Assertion failure.");
    }
}
 
/////////////////////////////////////////////////////////////////////
#pragma mark ----- Data Structures -----
 
enum {
    kStreamDumpPerStreamDataMagic = 'StrD'
};
 
struct PerStreamData
{
    OSType              magic;              // must be kStreamDumpPerStreamDataMagic
    UInt16              streamID;
};
typedef struct PerStreamData PerStreamData, *PerStreamDataPtr;
 
static PerStreamDataPtr GetPerStreamData(queue_t* readOrWriteQ)
    // Get the per-stream data for the given queue.
    //
    // Environment: any standard STREAMS entry point
{
    PerStreamDataPtr streamData;
    
    streamData = (PerStreamDataPtr) readOrWriteQ->q_ptr;
 
    MoreAssertQ(streamData != nil);
    MoreAssertQ(streamData->magic == kStreamDumpPerStreamDataMagic);
    
    return streamData;
}
 
static char* gStreamList = nil;
 
static UInt16 gNextStreamID = 0;
 
/////////////////////////////////////////////////////////////////////
#pragma mark ----- Standard Entry Points -----
 
static SInt32 StreamDumpOpen(queue_t* rdq, dev_t* dev, SInt32 flag, SInt32 sflag, cred_t* creds)
    // This routine is called by STREAMS when a new stream is connected to
    // our module.
    //
    // Environment: standard STREAMS entry point
{
    int                 err;
    PerStreamDataPtr    streamData;
    
    err = noErr;
    if ( rdq->q_ptr != nil ) {
        goto done;
    }
    if ( (err == noErr) && (sflag != MODOPEN) ) {
        err = ENXIO;
    }
    if (err == noErr) {
        err = mi_open_comm(&gStreamList, sizeof(PerStreamData), rdq, dev, flag, sflag, creds);
        if ( err == noErr ) {
            // Note that we can't call GetPerStreamData because the magic is not set up yet.
            streamData = (PerStreamDataPtr) rdq->q_ptr;
            
            OTMemzero(streamData, sizeof(PerStreamData));
            
            streamData->magic    = kStreamDumpPerStreamDataMagic;
            streamData->streamID = OTAtomicAdd16(1, (SInt16 *) &gNextStreamID);
        }
    }
 
done:
    return err;
}
 
static SInt32 StreamDumpClose(queue_t* rdq, SInt32 flags, cred_t* credP)
    // This routine is called by STREAMS when a stream is being
    // disconnected from our module (ie closed).
    //
    // Environment: standard STREAMS entry point
{
    #pragma unused(flags)
    #pragma unused(credP)
 
    (void) mi_close_comm(&gStreamList, rdq);
 
    return 0;
}
 
static Boolean IsReadQ(queue_t* q)
    // Returns true if q is the read queue of a queue pair.
{
    return ( (q->q_flag & QREADR) != 0 );
}
 
static void DumpCommon(queue_t* q, mblk_t* mp)
    // Common code, called by both StreamDumpWritePut and 
    // StreamDumpReadPut, that logs the message via strlog.
{
    PerStreamDataPtr    streamData;
    const char *        primStr;
    const char *        qStr;
    char                lineBuffer[256];
 
    streamData = GetPerStreamData(q);
    
    primStr = nil;
    if ( mp->b_datap->db_type == M_DATA ) {
        primStr = "M_DATA";
    } else if ( mp->b_datap->db_type == M_PROTO) {
        switch ( ((T_primitives *) mp->b_rptr)->primType ) {
            case T_UNITDATA_IND:
                primStr = "T_UNITDATA_IND";
                break;
            case T_UNITDATA_REQ:
                primStr = "T_UNITDATA_REQ";
                break;
            case T_DATA_IND:
                primStr = "T_DATA_IND";
                break;
            case T_DATA_REQ:
                primStr = "T_DATA_REQ";
                break;
            default:
                // do nothing
                break;
        }
 
        // Always move to the next mblk because if primType is not 
        // a data message then primStr is still nil and we never look at 
        // mp.
        
        mp = mp->b_cont;
    }
    if (primStr != nil) {
        if ( IsReadQ(q) ) {
            qStr = "Read";
        } else {
            qStr = "Write";
            q = RD(q);
        }
 
        // Log some basic info about the message.
                
        (void) strlog(9993, streamData->streamID, kOTLvlInfoOnly, SL_TRACE | SL_NOTE, "%s (%s)", primStr, qStr);
        
        // Log the contents of each message block.
        
        while (mp != nil) {
            UInt32  mpSize;
            UInt32  i;
            UInt32  numPrintables;
            
            mpSize = MBLK_SIZE(mp);
            (void) strlog(9993, streamData->streamID, kOTLvlInfoOnly, SL_TRACE | SL_NOTE,
                             "%*m", mpSize, mp->b_rptr);
            
            // If the message block's data is too big for our line buffer, don't 
            // even attempt to log it as ASCII.
            
            if ( mpSize < 256 ) {
                BlockMoveData(mp->b_rptr, lineBuffer, mpSize);
                lineBuffer[mpSize] = 0;         // terminate string will null
                numPrintables = 0;
                for (i = 0; i < mpSize; i++) {
                    if ( lineBuffer[i] >= 32 && lineBuffer[i] < 127 ) {
                        numPrintables += 1;
                    } else {
                        // Substitute '.' for non-printable characters.
                        // CR and LF are printable (for the purposes of deciding whether 
                        // or not to print the ASCII at all).
                        if ( lineBuffer[i] == 13 || lineBuffer[i] == 10 ) {
                            numPrintables += 1;
                        }
                        lineBuffer[i] = '.';
                    }
                }
                if (numPrintables == mpSize) {
                    (void) strlog(9993, streamData->streamID, kOTLvlInfoOnly, SL_TRACE | SL_NOTE, "Ò%sÓ", lineBuffer);
                }
            }
            mp = mp->b_cont;
        }
    }
}
 
static SInt32 StreamDumpWritePut(queue_t* q, mblk_t* mp)
    // This routine is called by STREAMS when it has a message for our
    // module from upstream.  We call DumpCommon and then pass the 
    // along.
    //
    // Environment: standard STREAMS entry point
{
    DumpCommon(q, mp);
    putnext(q, mp);
    
    return 0;
}
 
static SInt32 StreamDumpReadPut(queue_t* q, mblk_t* mp)
    // This routine is called by STREAMS when it has a message for our
    // module from downstream.  We call DumpCommon and then pass the 
    // message along.
    //
    // Environment: standard STREAMS entry point
{
    DumpCommon(q, mp);
    putnext(q, mp);
 
    return 0;
}
 
/////////////////////////////////////////////////////////////////////
#pragma mark ----- Static Declaration Structures -----
 
static struct module_info gModuleInfo =  
{
    9993,                       // Module Number, only useful for debugging
    "OTStreamDumper",           // Name of module
    0,                          // Minimum data size
    INFPSZ,                     // Maximum data size
    16384,                      // Hi water mark for queue
    4096                        // Lo water mark for queue
};
 
static struct qinit gReadInit = 
{
    StreamDumpReadPut,          // Put routine for "incoming" data
    nil,                        // Service routine for "incoming" data
    StreamDumpOpen,             // Our open routine
    StreamDumpClose,            // Our close routine
    nil,                        // No admin routine
    &gModuleInfo                // Our module_info
};
 
static struct qinit gWriteInit =
{
    StreamDumpWritePut,         // Put routine for client data
    nil,                        // Service routine for client data
    nil,                        // open  field only used in read-side structure
    nil,                        // close field only used in read-side structure
    nil,                        // admin field only used in read-side structure
    &gModuleInfo                // Our module_info
};
 
static struct streamtab theStreamTab = 
{
    &gReadInit,                 // Our read-side qinit structure
    &gWriteInit,                // Our write-side qinit structure
    0,                          // We are not a mux, so set this to nil
    0                           // We are not a mux, so set this to nil
};
 
/////////////////////////////////////////////////////////////////////
#pragma mark ----- Macintosh-specific Static Structures -----
 
static struct install_info theInstallInfo =
{
    &theStreamTab,          // Stream Tab pointer
    kOTModIsModule + kOTModUpperIsTPI + kOTModIsFilter,
                            // Tell OT that we are a driver, not a module 
                            // TPI may not be right if we're pushed on a DLPI 
                            // stream, but OT doesn't look at that flag anyway.
    SQLVL_MODULE,           // Synchronization level, module level for the moment
    0,                      // Shared writer list buddy
    0,                      // Open Transport use - always set to 0
    0                       // Flag - always set to 0
};
 
// Prototypes for the exported routines below.
 
extern Boolean InitStreamModule(void *portInfo);
extern void TerminateStreamModule(void);
extern install_info* GetOTInstallInfo();
 
#pragma export list GetOTInstallInfo, InitStreamModule, TerminateStreamModule
 
// Export entry point
 
extern Boolean InitStreamModule(void *portInfo)
    // Initialises the module before the first stream is opened.
    // Should return true if the module has started up correctly.
    //
    // Environment: Always called at SystemTask time.
{   
    #pragma unused(portInfo)
 
    // Drop into MacsBug so that we can set breakpoints if necessary.
        
    #if MORE_DEBUG
        DebugStr("\pOTStreamDumper: InitStreamModule");
    #endif
 
    return true;
}
 
extern void TerminateStreamModule(void)
    // Shuts down the module after the last stream has been
    // closed.
    //
    // Environment: Always called at SystemTask time.
{
}
 
extern install_info* GetOTInstallInfo()
    // Return pointer to install_info to STREAMS.
{
    return &theInstallInfo;
}