/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    cntxtlog.h

Abstract:

    This module implements more logging for setupapi

Author:

    Gabe Schaffer (t-gabes) 7-Jul-1998

Revision History:

    Jamie Hunter (jamiehun) 26-Aug-1998

--*/

/*
    There are two log levels. One is set in the registry, which determines
    what is logged and how. The other is passed as a parameter, and indicates
    under what conditions the entry should be logged. For example, the registry
    would say that all errors and warnings should be logged, while the call to
    WriteLogEntry would specify in the LogLevel parameter that some string
    should be logged as a warning.

    The 24 least-significant bits specify flags which indicate the type of
    message when passed as a parameter, or the type of messages to logged
    when set in the registry.

    The 4 most-significant bits are flags:

    SETUP_LOG_DEBUGOUT - when specified in the registry, indicates that all
        log output should be sent to the debugger as well as the logfile.
        This flag is currently not supported as a parameter to WriteLogEntry.

    SETUP_LOG_SIMPLE - when specified in the registry, indicates that all
        logfile entries should be appended to the logfile in chronological
        order, rather than grouped by section name. This flag is not
        currently supported as a parameter.

    SETUP_LOG_BUFFER - when specified as a parameter, indicates that the
        message to be logged will be buffered until such time as a call to
        WriteLogEntry with the same LogContext but this flag is not specified.
        When an entry is to be logged without this flag set, the latest
        message will be added to the buffer, and the buffer will be flushed to
        the logfile.

        This allows you to build up a string to log without having to do
        buffer management everywhere.

        This flag does not make sense in the registry.

        NOTE: for proper functioning, do not mix log levels while buffering
        output.

        NOTE: if this flag is NOT specified, the string to be logged MUST
        end in a newline, otherwise bad stuff will happen if another log
        context writes to the logfile immediately afterward.

    SETUP_LOG_IS_CONTEXT - when specified in the registry, indicates that
        all context messages should be immediately logged to the logfile,
        rather than buffered in the LogContext.

        This flag should never appear in a file other than cntxtlog.*;
        that is, as a parameter it only makes sense if added to a multiple
        of SETUP_FIRST_LOG_CONTEXT.

        NOTE: the context buffering mechanism is explained below where
        ContextInfo is defined in SETUP_LOG_CONTEXT.
*/

//
// These registry/filename strings should probably belong elsewhere
//
#define SP_REGKEY_LOGLEVEL      TEXT("LogLevel")
#define SP_REGKEY_LOGPATH       TEXT("LogPath")
#define SP_REGKEY_APPLOGLEVEL   TEXT("AppLogLevels")
#define SP_LOG_FILENAME         TEXT("setupapi.log")


//
// these are for general setup log entries
//
#define SETUP_LOG_SHIFT         (0)
#define SETUP_LOG_LEVELMASK     0x000000FF
#define SETUP_LOG_NOLOG         0x00000001 // indicates no-logging (remember 0 is default)
#define SETUP_LOG_ERROR         0x00000010 // 10-1f is varying levels of errors
#define SETUP_LOG_WARNING       0x00000020 // 20-2f is varying levels of warnings
#define SETUP_LOG_INFO          0x00000030 // 30-3f is varying levels of info
#define SETUP_LOG_VERBOSE       0x00000040 // 40-4f is varying levels of verbose
#define SETUP_LOG_TIME          0x00000050 // 50+ allow logging of time-stamped enties
#define SETUP_LOG_TIMEALL       0x00000060 // 60+ turns on time-stamping of all entries
#define SETUP_LOG_VVERBOSE      0x00000070 // 70-7f is very-verbose - reserved for stuff that logging really slows down
#define SETUP_LOG_DEFAULT       0x00000020

//
// these are for driver-only log entries
//
#define DRIVER_LOG_SHIFT        (8)
#define DRIVER_LOG_LEVELMASK    0x0000FF00
#define DRIVER_LOG_NOLOG        0x00000100 // indicates no-logging (remember 0 is default)
#define DRIVER_LOG_ERROR        0x00001000
#define DRIVER_LOG_WARNING      0x00002000
#define DRIVER_LOG_INFO         0x00003000
#define DRIVER_LOG_INFO1        0x00003100
#define DRIVER_LOG_VERBOSE      0x00004000
#define DRIVER_LOG_VERBOSE1     0x00004100
#define DRIVER_LOG_TIME         0x00005000
#define DRIVER_LOG_TIMEALL      0x00006000
#define DRIVER_LOG_VVERBOSE     0x00007000 // 70-7f is very-verbose - reserved for stuff that logging really slows down
#define DRIVER_LOG_DEFAULT      0x00003000

//
// Calling AllocLogInfoSlot will return an index with SETUP_LOG_IS_CONTEXT set
// this index represents a nested stack entry for logging information
// that will get dumped if an actual log entry is dumped
// thus providing more information
// note that lower 16 bits are used for index, re-using above Log level bits
//
#define SETUP_LOG_IS_CONTEXT    0x10000000
#define SETUP_LOG_CONTEXTMASK   0x0000ffff

//
// pass this flag to WriteLogEntry to have the entry stored in a buffer,
// to be spit out the next time the flag is *not* specified
//
#define SETUP_LOG_BUFFER    0x20000000

//
// pass this flag to registry to have output sent to debugger
//
#define SETUP_LOG_DEBUGOUT  0x80000000
//
// pass this flag to registry to have entries simply appended to the log
//
#define SETUP_LOG_SIMPLE    0x40000000
//
// pass this flag to registry to speed up logging
//
#define SETUP_LOG_NOFLUSH   0x20000000
//
// pass this flag to registry to indicate we want to always log SETUP_LOG_ISCONTEXT
//
#define SETUP_LOG_ALL_CONTEXT 0x10000000
//
// pass this flag to registry to indicate we always want to log timestamp
//
#define SETUP_LOG_TIMESTAMP 0x08000000
//
// for validating registry log value
//
#define SETUP_LOG_VALIDREGBITS (SETUP_LOG_DEBUGOUT|SETUP_LOG_SIMPLE|SETUP_LOG_NOFLUSH|SETUP_LOG_ALL_CONTEXT|SETUP_LOG_TIMESTAMP|DRIVER_LOG_LEVELMASK|SETUP_LOG_LEVELMASK)
//
// for validating non-context log value
//
#define SETUP_LOG_VALIDLOGBITS (SETUP_LOG_DEBUGOUT|SETUP_LOG_BUFFER|DRIVER_LOG_LEVELMASK|SETUP_LOG_LEVELMASK)
//
// for validating context log value
//
#define SETUP_LOG_VALIDCONTEXTBITS (SETUP_LOG_IS_CONTEXT | SETUP_LOG_CONTEXTMASK)


//
// timeouts when waiting for log acquired by another process
//
#define MAX_LOG_INTERVAL        (1000)       // 1s
#define MAX_LOG_WAIT            (10 * MAX_LOG_INTERVAL)


//
// This is the structure that holds all of the data required to persist logging
// information. It is not to be confused with the SETUPLOG_CONTEXT struct that
// is used elsewhere in setup.
//
typedef struct _SETUP_LOG_CONTEXT {
    //
    // Pointer to allocated name of section to be used.
    // If NULL, a section name will be generated on first use.
    //
    PTSTR       SectionName;

    //
    // Multiple structures may simultaneously have pointers to
    // this struct, so a ref count is needed. CreateLogContext()
    // will set this to 1, and DeleteLogContext() will decrement
    // this until it reaches 0 (at which point the structure is
    // actually freed).
    //
    UINT        RefCount;

    //
    // This is the number of entries that have been logged in this
    // context. If timestamp is used for the section name, this will
    // allow us to use the time of the first error, rather than the
    // time the log context was created.
    //
    UINT        LoggedEntries;

    //
    // These fields are for implementation of
    // AllocLogSlot and ReleaseLogSlot functions
    // ContextInfo is a list of strings indexable via SETUP_LOG_CONTEXTMASK
    // bits of a context slot returned by AllocLogSlot (ie, the slot)
    // it is also enumeratable via the link list headed by ContextFirstUsed
    // ContextFirstUsed points to first slot currently in use (bottom of stack)
    // ContextIndexes[ContextFirstUsed] points to next and so on
    // ContextLastUnused points to last slot that was released
    // -1 is used as end of list value
    // An entry may disappear in the middle of the stack if the context is used in
    // more than one thread
    //
    PTSTR       *ContextInfo;       // pointer to array of strings
    int         *ContextIndexes;    // by mark, is either index to ContextInfo, or to next unused mark
    int         ContextLastUnused;  // LIFO linked list of unused marks
    int         ContextBufferSize;  // items allocated for
    int         ContextFirstUsed;   // FIFO linked list of used marks
    int         ContextFirstAuto;   // FIFO linked list of auto-release used marks

    //
    // Sometimes multiple strings need to be logged as one entry, which
    // requires making multiple calls to WriteLogEntry. If SETUP_LOG_BUFFER
    // is specified, the text will be accumulated in the buffer until such
    // time as SETUP_LOG_BUFFER is not specified, in which case the
    // contents of Buffer is output together with the current string.
    //
    PTSTR       Buffer;

    //
    // In case multiple threads access this struct simultaneously,
    // access to ContextInfo must be serialized. Also, we
    // don't want this to be deleted while another thread is using it.
    //

} SETUP_LOG_CONTEXT, *PSETUP_LOG_CONTEXT;

//
// TLS data used by logging
//
typedef struct _SETUP_LOG_TLS {
    //
    // stack of context's
    //
    PSETUP_LOG_CONTEXT  ThreadLogContext;

} SETUP_LOG_TLS, *PSETUP_LOG_TLS;

//
// Convenient macro for determining whether a deletion failure should be
// considered an error or a verbose entry.
//
#define DEL_ERR_LOG_LEVEL(Err) ((((Err) == ERROR_FILE_NOT_FOUND) || ((Err) == ERROR_PATH_NOT_FOUND)) \
                                ? SETUP_LOG_VERBOSE : SETUP_LOG_ERROR)

DWORD
CreateLogContext(
    IN PCTSTR SectionName,              OPTIONAL
    IN  BOOL UseDefault,
    OUT PSETUP_LOG_CONTEXT *LogContext
    );

VOID
DeleteLogContext(
    IN PSETUP_LOG_CONTEXT LogContext
    );

DWORD
RefLogContext(  // increment reference count
    IN PSETUP_LOG_CONTEXT LogContext
    );

VOID
SetLogSectionName(
    IN PSETUP_LOG_CONTEXT LogContext,
    IN PCTSTR SectionName
    );

DWORD
InheritLogContext(
    IN TRACK_ARG_DECLARE TRACK_ARG_COMMA
    IN PSETUP_LOG_CONTEXT Source,
    OUT PSETUP_LOG_CONTEXT *Dest
    );

DWORD
ShareLogContext(
    IN OUT PSETUP_LOG_CONTEXT *Primary,
    IN OUT PSETUP_LOG_CONTEXT *Secondary
    );

DWORD
pSetupWriteLogEntry(
    IN PSETUP_LOG_CONTEXT LogContext,   OPTIONAL
    IN DWORD Level,
    IN DWORD MessageId,
    IN PCTSTR MessageStr,               OPTIONAL
    ...                                 OPTIONAL
    );

VOID
pSetupWriteLogError(
    IN PSETUP_LOG_CONTEXT LogContext,   OPTIONAL
    IN DWORD Level,
    IN DWORD Error
    );

#define WriteLogEntry pSetupWriteLogEntry
#define WriteLogError pSetupWriteLogError

DWORD
MakeUniqueName(
    IN  PCTSTR Component,        OPTIONAL
    OUT PTSTR * UniqueString
    );

DWORD
AllocLogInfoSlot(
    IN PSETUP_LOG_CONTEXT LogContext,
    IN BOOL               AutoRelease
    );

DWORD
AllocLogInfoSlotOrLevel(
    IN PSETUP_LOG_CONTEXT LogContext,
    IN DWORD              Level,
    IN BOOL               AutoRelease
    );

VOID
ReleaseLogInfoSlot(
    IN PSETUP_LOG_CONTEXT LogContext,
    DWORD Slot
    );

BOOL
InitializeContextLogging(
    IN BOOL Attach
    );

BOOL
ContextLoggingTlsInit(
    IN BOOL Init
    );

BOOL
SetThreadLogContext(
    IN PSETUP_LOG_CONTEXT LogContext,
    OUT PSETUP_LOG_CONTEXT *PrevContext   OPTIONAL
    );

PSETUP_LOG_CONTEXT
GetThreadLogContext(
    );