aboutsummaryrefslogtreecommitdiffstats
path: root/lib/os_mon/c_src/nteventlog/elog_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/os_mon/c_src/nteventlog/elog_main.c')
-rw-r--r--lib/os_mon/c_src/nteventlog/elog_main.c504
1 files changed, 504 insertions, 0 deletions
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<rklen; ++i){
+ elog[i] = OpenEventLog(NULL, rk[i].facility_name);
+
+ if(elog[i] == NULL){
+ fprintf(stderr,
+ "%s: Could not open \"%s\" eventlog.\n",
+ argv[0], rk[i].facility_name);
+ return 1;
+ }
+ if((events[i] = CreateEvent(NULL,FALSE,FALSE,NULL))
+ == NULL){
+ fprintf(stderr,"%s: Could not create event object.\n", argv[0]);
+ return 1;
+ }
+ if(!NotifyChangeEventLog(elog[i],events[i])){
+ fprintf(stderr,"%s: Could not get eventlog notification.\n", argv[0]);
+ return 1;
+ }
+ if(read_from(&(rk[i].latest_record),
+ &(rk[i].latest_time),
+ elog[i],
+ rk[i].facility_name) > 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<rklen;++i){
+ if(read_from(&(rk[i].latest_record),
+ &(rk[i].latest_time),
+ elog[i],
+ rk[i].facility_name) > 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;
+}