From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- lib/os_mon/c_src/nteventlog/elog_main.c | 504 ++++++++++++++++++++++++++++++++ 1 file changed, 504 insertions(+) create mode 100644 lib/os_mon/c_src/nteventlog/elog_main.c (limited to 'lib/os_mon/c_src/nteventlog/elog_main.c') diff --git a/lib/os_mon/c_src/nteventlog/elog_main.c b/lib/os_mon/c_src/nteventlog/elog_main.c new file mode 100644 index 0000000000..f79f32c8ef --- /dev/null +++ b/lib/os_mon/c_src/nteventlog/elog_main.c @@ -0,0 +1,504 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Module: elog_main + * Purpouse: Main program and logic in the nteventlog program + * which waits for logging events and sends them to erlang. + * At startup, the registry keys for the identifier given on the + * command line are read to see what log record was the last + * displayed for this identifier. All "new" records are then + * displayed. After that the program waits for new events to arrive + * and displays them (sends them to erlang). If stdin is a pipe + * (not a console) an ACK is requested for every entry displayed. + * when the ACK is received, the registry entries for the identifier + * is updated, so that next time the program starts, only the new + * entries are displayed. + */ +#include "elog_global.h" +#include "elog_util.h" +#include "elog_pipe_stdin.h" +#include "elog_format.h" +#include "elog_registry.h" + +/* + * A race condition in the event log notification and the + * event log reading results in us having to retry reading the + * eventlog after some time. One second seems to do it... + */ +#define RETRY_TIMEOUT 1000 + +/* + * Constants for the logging formats. + */ +#define LOG_FORMAT "%s [%s] %s, %s: %s\n" +#define PIPE_LOG_FORMAT "%dH%s%dH%s%dH%s%dH%s%dH%s" +#define PIPE_LOG_ACK "A" +#define PIPE_LOG_EXTRA (5*10) /* 5 int */ +#define ACK_MAX 100 +#define TIME_FORMAT "%d-%b-%Y %H:%M:%S GMT" +/* 2 3 4 2 2 2 */ +#define TIME_BUFSIZ 25 +#define CANT_FORMAT_MESSAGE "[EventLogReader unable to format message text]" + +/* the default registry identification */ +#define DEFAULT_IDENT "DefaultIdent" + +/*#define HARDDEBUG*/ + +/* Flag set if eventlog is purged and need reopening.*/ +static int reopen_event_log = 0; + +/* + * Calculates the needed buffersize for a record in the eventlog. + * if recordnum == 0, looks at next record, otherwise looks + * at the record with the given number. + */ +static DWORD needed(HANDLE elog, DWORD recordnum){ + EVENTLOGRECORD dummy; + DWORD red, need; + DWORD last_error; + DWORD flags = + EVENTLOG_FORWARDS_READ | + ((recordnum) ? EVENTLOG_SEEK_READ : EVENTLOG_SEQUENTIAL_READ); +#ifdef HARDDEBUG + fprintf(stderr,"Calculating need when recordnum = %lu\n",recordnum); +#endif + if(!ReadEventLog(elog, + flags, + recordnum, + &dummy, + (DWORD) sizeof(EVENTLOGRECORD), + &red, + &need) && + (last_error = GetLastError()) == ERROR_INSUFFICIENT_BUFFER){ + return need; + } else if(last_error == ERROR_EVENTLOG_FILE_CHANGED){ + reopen_event_log = 1; + return (DWORD) 0; + } else { +#ifdef HARDDEBUG + output_error(last_error,"needed() failed to read eventlog"); +#endif + return (DWORD) 0; + } +} + + +/* + * Checks (any type of) stdin for end of file. + * Expects data present on stdin. + */ +BOOL eof_on_stdin(void){ + HANDLE in = GetStdHandle(STD_INPUT_HANDLE); + char x[1]; + DWORD y; + INPUT_RECORD input; + DWORD red; + static int state = 1; /* Return pressed = 1, ^Z after that = 2 */ + + if(!console_stdin()){ + return peek_pipe_stdin_eof(); + } + /* Console input, may be just about every type of event, look for + ^Z pressed... */ + if(!ReadConsoleInput(in,&input,1,&red)) + return FALSE; + if(input.EventType != KEY_EVENT) + return FALSE; + if(input.Event.KeyEvent.bKeyDown){ + switch(input.Event.KeyEvent.uChar.AsciiChar){ + case 0: + break; + case 13: + x[0] = '\r'; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),"\r\n",2,&y,NULL); + if(state == 2) + return TRUE; + else + state = 1; + break; + case 26: + if(state == 1) + state = 2; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),"^Z",2,&y,NULL); + break; + default: + if(((unsigned char) input.Event.KeyEvent.uChar.AsciiChar) < ' '){ + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),"^",1,&y,NULL); + x[0] = input.Event.KeyEvent.uChar.AsciiChar + '@'; + } else + x[0] = input.Event.KeyEvent.uChar.AsciiChar; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),x,1,&y,NULL); + state = 0; + break; + } + return FALSE; + } + return FALSE; +} + +/* + * Writes eventlog entries to erlang and requires ACK for + * each record. + */ +BOOL data_to_pipe(char *string, char *answer, int answer_siz){ + unsigned char len[2]; + unsigned char *ptr; + int siz = strlen(string); + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE in = GetStdHandle(STD_INPUT_HANDLE); + DWORD written, red; + DWORD left; + + len[0] = (siz >> 8) & 0xFF; + len[1] = siz & 0xFF; + if(!WriteFile(out, len, 2, + &written, NULL) || written != 2) + return FALSE; + if(!WriteFile(out, string, siz, &written, NULL) || + written != (DWORD) siz) + return FALSE; + /* Read ACK from erlang */ + left = 2; + ptr = len; + for(;;){ + if(!(red = read_pipe_stdin(ptr, left))) + return FALSE; + else if(red < left){ + ptr += red; + left -= red; + } else { + break; + } + } + siz = len[0] << 8 | len[1]; + + if(siz >= answer_siz - 1) + return FALSE; + + left = siz; + ptr = (unsigned char *) answer; + for(;;){ + if(!(red = read_pipe_stdin(ptr, left))){ + return FALSE; + } else if(red < left){ + ptr += red; + left -= red; + } else { + break; + } + } + answer[siz] = '\0'; + return TRUE; +} + +/* + * The actual writing of records. + * Behaves differently if stdout is a pipe (erlang) + * or a console (test run). + */ + +BOOL output_record(char *category, EVENTLOGRECORD *event){ + char *strbeg, *fac, *sev; + char eventfilename[MAX_PATH]; + char paramfilename[MAX_PATH]; + char bigbuff[BIGBUFSIZ]; + MessageFiles mf; + char tbuff[TIME_BUFSIZ]; + BOOL ret; + DWORD written; + char *buff; + char ackbuff[ACK_MAX]; + + mf = get_messagefiles(category,((char *)event)+sizeof(EVENTLOGRECORD), + eventfilename, MAX_PATH, + paramfilename, MAX_PATH); + if(!mf.event){ + strcpy(bigbuff, CANT_FORMAT_MESSAGE); + } else { + strbeg = (char *) event; + strbeg += event->StringOffset; + if(!format_message(mf, event->EventID, + strbeg, event->NumStrings, bigbuff, BIGBUFSIZ)){ + strcpy(bigbuff, CANT_FORMAT_MESSAGE); + } + } + fac = ((char *)event)+sizeof(EVENTLOGRECORD); + sev = lookup_severity(event->EventType); + if(console_stdin()){ + *tbuff = '\0'; + strftime(tbuff, (size_t) TIME_BUFSIZ, TIME_FORMAT, + gmtime((time_t *)&(event->TimeGenerated))); + buff = + malloc(strlen(bigbuff) + TIME_BUFSIZ /* date-time */ + + strlen(category) + + strlen(fac) /* facility */ + + strlen(sev) /*severity */+ + strlen(LOG_FORMAT) /* More than actually needed */ + 1); + sprintf(buff, LOG_FORMAT, tbuff, fac, category, sev, bigbuff); + ret = WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buff, strlen(buff), + &written, NULL); /* Overlapped structure for stdout? */ + free(buff); + } else { /* pipe */ + sprintf(tbuff,"%lu", event->TimeGenerated); + buff = malloc(strlen(bigbuff) + + strlen(tbuff) + + strlen(category) + + strlen(fac) + + strlen(sev) + + strlen(PIPE_LOG_FORMAT) + + PIPE_LOG_EXTRA); + sprintf(buff,PIPE_LOG_FORMAT, + strlen(tbuff),tbuff, + strlen(category), category, + strlen(fac), fac, + strlen(sev), sev, + strlen(bigbuff), bigbuff); + ret = data_to_pipe(buff,ackbuff, ACK_MAX); + if(ret && strcmp(ackbuff,PIPE_LOG_ACK)) + ret = FALSE; + free(buff); + } + return ret; +} + + +/* + * Read eventlog entries FOLLOWING the given record + * number and timestamp, and sends them to + * stdout. If timestamp does + * not correspond with record number, the + * log is concidered wrapped around + * and is reread from the beginning. + * time is ignored if 0. + * If record_number is 0, the whole log is read (if there is one). + * If the function is unsuccessful, the global variable + * reopen_event_log may be set to TRUE, which means + * that the eventlog has changed and has to be reopened. + * It's the callers responsibility to set reopen_event_log to + * 0 before calling. + */ +static int read_from(DWORD *record_number, DWORD *time, + HANDLE elog, char *category){ + DWORD dummy; + static EVENTLOGRECORD *event = NULL; + static DWORD eventsiz = 0; + DWORD red, need; + /* Always begin reading from record 1, record 0 never exists */ + DWORD tmp = (*record_number) ? *record_number : 1; + DWORD ttmp = *time; + int skip = !!(*record_number); /* Dont skip if record_number == 0 */ + int maybe_done = 0; + int i; + + /* First try seeking to the correct place in the eventlog */ + /* the variable skip tells us that we are rereading the */ + /* last read eventlog record and the variable */ + /* maybe_done tells us that we have read the last eventlog */ + /* and an EOF is OK. */ + for(;;){ + if(!(need = needed(elog, tmp))){ + if(maybe_done) /* Has read one correct record + and are satisfied with that. */ + return 0; + /* Hmm, could not find the record? Try the oldest... */ + if(!GetOldestEventLogRecord(elog, &tmp) || + !(need = needed(elog, tmp))){ + /* Something's terribly wrong. */ +#ifdef HARDDEBUG + fprintf(stderr,"Could not get oldest eventlog record!\n", + need); +#endif + return -1; + } + skip = 0; + } + /* need == number of bytes for this record, + tmp == this recordnumber */ + if(!event) + event = malloc(eventsiz = need); + else if(eventsiz < need) + event = realloc(event, eventsiz = need); + if(!ReadEventLog(elog, + (DWORD) (EVENTLOG_FORWARDS_READ | + EVENTLOG_SEEK_READ), + tmp, + event, + need, + &red, + &dummy)){ + if(GetLastError() == ERROR_EVENTLOG_FILE_CHANGED){ + reopen_event_log = 1; + } +#ifdef HARDDEBUG + output_error(GetLastError(), + "Failed first eventlog read in read_from\n"); +#endif + return -1; + } + if(skip){ + if(ttmp && event->TimeWritten != ttmp){ + /* Wrapped around eventlog */ + tmp = 1; + } else { + maybe_done = 1; + ++tmp; + } + skip = 0; + } else + break; + } + /* Got the first record in buffer, display and continue reading */ + /* sequentially from here. */ + for(i=1;;++i){ + if(!output_record(category, event)) + return -1; + *record_number = event->RecordNumber; + *time = event->TimeWritten; + if(!(need = needed(elog,(DWORD) 0))) + break; /* End of log */ + if(eventsiz < need) + event = realloc(event, eventsiz = need); + if(!ReadEventLog(elog, + (DWORD) EVENTLOG_FORWARDS_READ | + EVENTLOG_SEQUENTIAL_READ, + (DWORD) 0, + event, + need, + &red, + &dummy)){ + if(GetLastError() == ERROR_EVENTLOG_FILE_CHANGED){ + reopen_event_log = 1; + } + return -1; + } + } + return i; +} + +/* + * Read unread events and wait for new to arrive. + */ +int main(int argc, char **argv){ + HANDLE elog[NUM_CATEGORIES]; + HANDLE events[NUM_CATEGORIES + 1]; /* The stdin handle goes + in here to */ + int eventlen; + char *ident; + DWORD record = 0, time = 0; + RegKeys rk[NUM_CATEGORIES]; + int rklen = NUM_CATEGORIES; + int i; + int ret; + int x = 0; + int retry = 0; + + if(argc < 2){ + ident = DEFAULT_IDENT; + } else { + ident = argv[1]; + } + + if(!setup_pipe_stdin()){ + fprintf(stderr,"%s: Stdin could not be initialized.\n",argv[0]); + return 1; + } + if(get_regkeys(ident, rk, &rklen) != 0){ + fprintf(stderr, + "%s: Could not get/create registry parameters.\n", argv[0]); + return 1; + } + for(;;){ + for(i=0; i 0) + set_regkeys(ident, rk + i, 1); + } + eventlen = rklen; + events[eventlen] = get_stdin_event(); + ++eventlen; + + for(;;){ +#ifdef HARDDEBUG + fprintf(stderr,"Entering Wait...\n"); +#endif + reopen_event_log = 0; + ret = WaitForMultipleObjects(eventlen, + events, + FALSE, + (retry) ? + RETRY_TIMEOUT : + INFINITE); +#ifdef HARDDEBUG + fprintf(stderr,"Wait returned!\n"); +#endif + if(ret == WAIT_TIMEOUT){ + if(!retry){ + fprintf(stderr,"%s: Timeout when no such possible!\n", + argv[0]); + return 1; + } + retry = 0; + } else { + if(((int) (ret - WAIT_OBJECT_0)) >= rklen && eof_on_stdin()) + goto done; + retry = 1; + } + for(i=0;i 0) + set_regkeys(ident, rk + i, 1); + } + if(reopen_event_log) + break; + } + for(i=0; i < rklen; ++i){ + CloseEventLog(elog[i]); + CloseHandle(events[i]); + } + } +done: +#ifdef DEBUG + fprintf(stderr,"%s: EOF\n", argv[0]); +#endif + for(i=0; i < rklen; ++i) + CloseEventLog(elog[i]); + return 0; +} -- cgit v1.2.3