#pragma module PPPD$LOGGER "X-3" /* ***************************************************************************** * * Copyright © 1996 Digital Equipment Corporation. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by Digital Equipment Corporation. The name of the * Corporation may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * ***************************************************************************** FACILITY: PPPD ABSTRACT: This routine provides the diagnostic logging capability. The logger runs as a detached process and is started up by the management utility. The management utility will pass in the mailbox name as the SYS$INPUT string and the log file name as the SYS$OUTPUT string when the process with the logger is started. The log process will create the log file and write binary log messages into the log file. Because we will be writing binary data the log file will be a variable length record file instead of stream file. The logger will continue to write log records until it encounters a fatal error or until it get a log record that indicates that it should stop logging. The logger works at two levels a main non-AST thread will write log records to the file. The AST thread will read the mailbox, queue the log records to the mainline and start another read on the mailbox. This scheme should be sufficiently quick enough to work for the ASYNCH ports. We may need to devise a faster mechanism when we and if we add support for ISDN lines. AUTHOR: Forrest A. Kenney 14-December-1995 REVISION HISTORY: X-3 BWK002 Barry W. Kierstein 17-DEC-1996 Replaced the standard Digital copyright with one compatible with the CMU copyright. X-2 BWK001 Barry W. Kierstein 26-Jul-1996 Moved mailbox delete code to the EOF message section so that the mailbox will not be deleted unless we have a normal shutdown of the PPP tracing. Changed the EFN used from 0 to 128. */ #pragma message disable (ALIGNEXT) /* Include various necessary description files */ #include #include #include #include #include #include #include #include #include #include #include "pppd_log_if.h" #include "pppdutil_common.h" /* Non-Shared module constants */ #define EFN PPPD_EFN #define FALSE 0 #define MAXNAME 255 #define TRUE 1 /* Define several gloabl structures */ typedef struct item { short int buff_size; short int item; char *buffaddr; short int *length; } ITEM; typedef struct io_buff /* I/O block used by logger code */ { int *flink; /* forward and backard queue links */ int *blink; short int status; /* IOSB used to mailbox read requests */ short int byte_cnt; int unused; logRecord record; } IO_BUFF; struct q_head /* Queue head structure */ { int *flink; int *blink; }; /* Forward routine references */ FILE *create_log_file (); int open_mailbox(); IO_BUFF *allocate_io_buffer (); void free_io_buffer (IO_BUFF *buffer); void mbx_read_ast (IO_BUFF *buffer); /* Global Variables*/ FILE *logfile; short int mbx_chan; int exiting = FALSE; int exit_status = SS$_NORMAL; struct q_head _align(QUADWORD) buffer_queue = {0,0}; struct q_head _align(QUADWORD) log_queue = {0,0}; /* **+ ** main - Main routine ** ** Functional Description: ** ** The program intitializes the environment and then hibernates waiting to ** be awakened. Once awakend it will remove a log record from the queue of ** log records. It will then examing the record and if the type is EOF it will ** set the exiting flag. Even if the log record is and EOF record it will be ** written to the log file. If it is not exiting it will see if there is ** another entry to remove. If no more entries it will hibernate once again. ** ** When it is all done it will close the log file, flush any outstanding ** I/O on the mailbox, and request that the mailbox be deleted. If any errors ** occured then the final exit status will be the error that triggered the ** early exit. Otherwise it will be any errors encountered as part of exiting. ** ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** exiting - Flag indicating if program is finishing up ** processing ** exit_status - Contains the reason while logging is stopping ** logfile - FILE pointer to the log file ** log_queue - Queue of I/O buffers to be written to log file ** mbx_chan - VMS channel pointing logger mailboc ** ** Local Variables: ** ** got_buf-status - Return status from removing entry from queue ** log_buff - Pointer to current log buffer being processed ** status - Return status from various routine calls ** ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ main() { int got_buf_status; int status; IO_BUFF *log_buff; if (!(status = open_mailbox() & SS$_NORMAL)) { return (status); } if (create_log_file() == NULL) { return (exit_status); } do { got_buf_status = LIB$REMQHI(&log_queue, &log_buff); while (got_buf_status & SS$_NORMAL) { fwrite((void *)&log_buff->record, log_buff->byte_cnt, 1, logfile); if (log_buff->record.msgType == LOG_EOF) { exiting = TRUE; /* No status is checked for these items as there is nothing */ /* of any value that can be done about them */ status = SYS$CANCEL(mbx_chan); #ifdef DEBUG_CODE log_buff->record.msgLen = sprintf ((char *)log_buff->record.msg, "Status for SYS$CANCEL = %8X", status); fwrite((void *)&log_buff->record, log_buff->byte_cnt, 1, logfile); #endif status = SYS$DELMBX(mbx_chan); #ifdef DEBUG_CODE log_buff->record.msgLen = sprintf ((char *)log_buff->record.msg, "Status for SYS$DELMBX = %8X", status); fwrite((void *)&log_buff->record, log_buff->byte_cnt, 1, logfile); #endif } free_io_buffer(log_buff); got_buf_status = LIB$REMQHI(&log_queue, &log_buff); } if (!exiting) SYS$HIBER(); } while ((!exiting) || (log_queue.flink != 0)); /* No status is checked for these items as there is nothing of any value that can be done about them */ fclose(logfile); if (exit_status != SS$_NORMAL) { status = exit_status; } return (status); } /* **+ ** allocate_io_buffer - Get a free I/O buffer from queue ** ** Functional Description: ** ** This routine is used to get a free I/O buffer from the queue of ** available I/O buffers. If the program is exiting then this routine will ** return with an error. ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** buffer_queue - Queue of free I/O buffer ** exiting - Flag indicating if program is finishing up ** processing ** ** Local Variables: ** ** buff_addr - Pointer to free I/O buffer if one was available ** status - Return status from LIB$REMQHI ** ** Outputs: ** ** return_status - Address of buffer or ** LIB$_QUEWASEMP no I/O buffer ** SS$_FORCEDEXIT program exiting ** ** NOTES: ** ** None ** **- */ IO_BUFF *allocate_io_buffer() { int status; IO_BUFF *buff_addr; if (!exiting) { status = LIB$REMQHI(&buffer_queue, &buff_addr); if (status & SS$_NORMAL) { return(buff_addr); } else { return((IO_BUFF *)malloc(sizeof(IO_BUFF))); } } else { return((IO_BUFF *)SS$_FORCEDEXIT); } } /* **+ ** create_log_file - Create a new log file ** ** Functional Description: ** ** This routine will create a new log file using the name obtained ** by translating the logical name SYS$OUTPUT. It will then open the log file. ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** exit_status - Contains the reason while logging is stopping ** logfile - File pointer log file that will be created ** ** Local Variables: ** ** attributes - Logical name translation attribute(s) ** item_lst - Item list to pass to $TRNLNM ** log_name - String descriptor with logical name to ** translate ** name_length - Length in byte of translated logical name ** name_string - Translated name ** status - Return status from call to $TRNLNM ** table_name - String descriptor with the logical name tables ** to be searched ** ** Outputs: ** ** FILE - C file pointer for the log file ** ** NOTES: ** ** None ** **- */ FILE *create_log_file() { $DESCRIPTOR(log_name,"SYS$OUTPUT"); $DESCRIPTOR(table_name,"LNM$PROCESS_TABLE"); int attributes = LNM$M_CASE_BLIND; char name_string[MAXNAME]; short int name_length; int status; ITEM item_lst[2]; item_lst[0].buff_size = MAXNAME; item_lst[0].item = LNM$_STRING; item_lst[0].buffaddr = name_string; item_lst[0].length = &name_length; item_lst[1].buff_size = 0; item_lst[1].item = 0; item_lst[1].buffaddr = 0; item_lst[1].length = 0; status = SYS$TRNLNM(&attributes, &table_name, &log_name, 0, &item_lst); if (status & SS$_NORMAL) { name_string[name_length] = (char) 0; logfile = fopen(name_string, "wb+", "ctx=rec", "rat=none", "rfm=var", "shr=get"); if (logfile == NULL) { exit_status = SS$_ABORT; /* Dummy error if file open failed */ } else { exit_status = SS$_NORMAL; } return (logfile); } else { exit_status = status; return ((FILE *) NULL); } } /* **+ ** free_io_buffer - Put I/O buffer on queue of available buffers ** ** Functional Description: ** ** This routine will place a free I/O buffer on the queue of available I/O ** buffers. ** ** Explicit Inputs: ** ** buff_addr - Pointer to I/O buffer to be placed on queue of ** free I/O buffers ** ** Implicit Inputs: ** ** buffer_queue - Queue of free I/O buffer ** exiting - Flag indicating if program is finishing up ** processing ** exit_status - Contains the reason while logging is stopping ** mbx_chan - Channel number for mailbox ** ** Local Variables: ** ** status - Return status from READ ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ void free_io_buffer (IO_BUFF *buff_addr) { int ast_stat; int status; if (!exiting) { LIB$INSQHI(buff_addr, &buffer_queue); } } /* **+ ** mbx_read_ast - Handle last log record and queue read for next one ** ** Functional Description: ** ** This routine is called when a logger record has been read from the ** mailbox. It makes sure that the read was successful and places the record ** in the queue of records to be written to the log file. If this is the first ** entry on the queue it also wakes up the main line code. If not it skips ** waking the mainline on the assumption that it is already awake. It then ** gets another buffer and queues another read to the loggers mailbox. This ** will continue until we get a sever error or until the exiting flag is set. ** ** Explicit Inputs: ** ** buff_addr - Pointer to I/O buffer ** ** Implicit Inputs: ** ** exiting - Flag indicating if program is finishing up ** processing ** exit_status - Contains the reason while logging is stopping ** log_queue - Queue of logger records to be written to the ** log file ** mbx_chan - Channel number for mailbox ** ** Local Variables: ** ** status - System status returns ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ void mbx_read_ast(IO_BUFF *buff_addr) { int status; if (!exiting) { if (buff_addr->status & SS$_NORMAL) { status = LIB$INSQTI(buff_addr, &log_queue); if (status = LIB$_ONEENTQUE) { SYS$WAKE (0, 0); } buff_addr = allocate_io_buffer(); if (((int)buff_addr != LIB$_QUEWASEMP) && ((int)buff_addr != SS$_FORCEDEXIT)) { status = SYS$QIO (EFN, mbx_chan, IO$_READVBLK, &buff_addr->status, mbx_read_ast, buff_addr, &buff_addr->record, sizeof(logRecord), 0, 0, 0, 0); if (!(status & SS$_NORMAL)) { exiting = TRUE; exit_status = status; SYS$WAKE (0, 0); } } } else { exiting = TRUE; exit_status = buff_addr->status; SYS$WAKE (0, 0); } } } /* **+ ** open_mailbox - Open the mailbox ** ** Functional Description: ** ** This routine will create a new log file using the name obtained ** by translating the SYS$OUTPUT. It will allocate a couple of logger buffers ** and place them on the queue of free buffers. Then it will then assign a ** channel to the mailbox and queue the first read. ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** exit_status - Contains the reason while logging is stopping ** mbx_chan - Channel number for mailbox ** ** Local Variables: ** ** attributes - Logical name translation attribute(s) ** buff_addr - Pointer to an I/O buffer ** devname - String descriptor used to hold the mailbox name ** i - Random loop counter ** item_lst - Item list to pass to $TRNLNM ** log_name - String descriptor with logical name to ** translate ** name_length - Length in byte of translated logical name ** name_string - Translated name ** status - Return status from call to SYS$TRNLNM ** table_name - String descriptor with the logical name tables ** to be searched ** ** Outputs: ** ** status - return status from various system calls. ** ** NOTES: ** ** None ** **- */ int open_mailbox() { $DESCRIPTOR(log_name,"SYS$INPUT"); $DESCRIPTOR(table_name,"LNM$PROCESS_TABLE"); int attributes = LNM$M_CASE_BLIND; char name_string[MAXNAME]; int i; int status; IO_BUFF *buff_addr; ITEM item_lst[2]; struct dsc$descriptor_vs devname; devname.dsc$w_maxstrlen = MAXNAME; devname.dsc$b_dtype = DSC$K_DTYPE_VT; devname.dsc$b_class = DSC$K_CLASS_VS; devname.dsc$a_pointer = name_string; item_lst[0].buff_size = MAXNAME; item_lst[0].item = LNM$_STRING; item_lst[0].buffaddr = name_string; item_lst[0].length = (short int *)&devname.dsc$w_maxstrlen; item_lst[1].buff_size = 0; item_lst[1].item = 0; item_lst[1].buffaddr = 0; item_lst[1].length = 0; status = SYS$TRNLNM(&attributes, &table_name, &log_name, 0, &item_lst); if (status & SS$_NORMAL) { for (i=0; i< 10; i++) { free_io_buffer((IO_BUFF *)malloc(sizeof(IO_BUFF))); } buff_addr = (IO_BUFF *)malloc(sizeof(IO_BUFF)); status = SYS$ASSIGN(&devname, &mbx_chan, 0, 0, 0); if (status & SS$_NORMAL) { status = SYS$QIO (EFN, mbx_chan, IO$_READVBLK, &buff_addr->status, mbx_read_ast, buff_addr, &buff_addr->record, sizeof(logRecord), 0, 0, 0, 0); } } return (status); }