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_format.c | 173 +++++++++ lib/os_mon/c_src/nteventlog/elog_format.h | 40 ++ lib/os_mon/c_src/nteventlog/elog_global.h | 57 +++ lib/os_mon/c_src/nteventlog/elog_main.c | 504 ++++++++++++++++++++++++++ lib/os_mon/c_src/nteventlog/elog_pipe_stdin.c | 151 ++++++++ lib/os_mon/c_src/nteventlog/elog_pipe_stdin.h | 90 +++++ lib/os_mon/c_src/nteventlog/elog_registry.c | 295 +++++++++++++++ lib/os_mon/c_src/nteventlog/elog_registry.h | 67 ++++ lib/os_mon/c_src/nteventlog/elog_util.c | 73 ++++ lib/os_mon/c_src/nteventlog/elog_util.h | 79 ++++ 10 files changed, 1529 insertions(+) create mode 100644 lib/os_mon/c_src/nteventlog/elog_format.c create mode 100644 lib/os_mon/c_src/nteventlog/elog_format.h create mode 100644 lib/os_mon/c_src/nteventlog/elog_global.h create mode 100644 lib/os_mon/c_src/nteventlog/elog_main.c create mode 100644 lib/os_mon/c_src/nteventlog/elog_pipe_stdin.c create mode 100644 lib/os_mon/c_src/nteventlog/elog_pipe_stdin.h create mode 100644 lib/os_mon/c_src/nteventlog/elog_registry.c create mode 100644 lib/os_mon/c_src/nteventlog/elog_registry.h create mode 100644 lib/os_mon/c_src/nteventlog/elog_util.c create mode 100644 lib/os_mon/c_src/nteventlog/elog_util.h (limited to 'lib/os_mon/c_src/nteventlog') diff --git a/lib/os_mon/c_src/nteventlog/elog_format.c b/lib/os_mon/c_src/nteventlog/elog_format.c new file mode 100644 index 0000000000..c9fb6b7e1a --- /dev/null +++ b/lib/os_mon/c_src/nteventlog/elog_format.c @@ -0,0 +1,173 @@ +/* + * %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% + */ +#include "elog_global.h" +#include "elog_format.h" + +/* + * The Newline treatment bits of FormatMessage + * This value should suppress all other than hardcoded newlines + */ +#define NL_TREATMENT FORMAT_MESSAGE_MAX_WIDTH_MASK + +/* + * Expands %%NNN formats in strings with strings from a + * ParameterMessageFile (open). + * A return of NULL means there's nothing to expand + * or that the buffer is to small, which probably means the + * same thing to the caller, that is use the + * original string as it is. + */ +static char *expand_message(char *toexpand, + HINSTANCE paramlib, + char *buff, int bufflen){ + char *oldpos; + int buffpos = 0; + char *pos = toexpand; + char *end; + unsigned long num; + char *replbuff = malloc(bufflen); + char *repl; + int replaced = 0; + + while((oldpos = pos, pos = strstr(pos,"%%"))){ + num = strtoul(pos + 2, &end, 0); + replaced = 1; + if(end == pos + 2 || num == 0){ + repl = "%%"; + } else { + if(!FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | + FORMAT_MESSAGE_IGNORE_INSERTS | + NL_TREATMENT, + (LPCVOID) paramlib, + (DWORD) num, + DEFAULT_LANGID, + replbuff, + (DWORD) bufflen, + NULL)){ + repl = ""; /* this is how the event logger treats it... */ + } else { + repl = replbuff; + } + } + if((int)(buffpos + strlen(repl) + (pos - oldpos) + 1) > bufflen){ + free(replbuff); + return NULL; + } + strncpy(&(buff[buffpos]),oldpos, pos - oldpos); + buffpos += pos - oldpos; + strcpy(&(buff[buffpos]), repl); + buffpos += strlen(repl); + pos = end; + } + free(replbuff); + if(!replaced) + return NULL; + if((int) (buffpos + strlen(oldpos) + 1) > bufflen) + return NULL; + strcpy(&(buff[buffpos]),oldpos); + return buff; +} + +/* + * A lot to free when returning from format_message, lets make it easier + */ +static char *fm_free_up(char **argv, char *tmpbuff, + char * tmpbuff2, + HINSTANCE elibrary, + HINSTANCE plibrary){ + if(plibrary != NULL){ + FreeLibrary(plibrary); + while(*argv) + free(*argv++); + } + free(tmpbuff); + free(tmpbuff2); + if(elibrary != NULL) + FreeLibrary(elibrary); + return NULL; +} + +#define FM_RETURN(X) \ +return (fm_free_up(argv, tmpbuff, tmpbuff2, elibrary, plibrary), (X)) + +/* + * Formats an eventlog message into a string buffer. + * Returns NULL if message could not be formatted (buffer to small or + * library error). + */ +char *format_message(MessageFiles mf, DWORD id, + char *strings, int numstrings, + char *buff, int bufflen){ + char *argv[MAX_PARAM_STRINGS]; + int argc,i; + HINSTANCE elibrary = NULL; + HINSTANCE plibrary = NULL; + char *tmpbuff = malloc(bufflen); + char *tmpbuff2 = malloc(bufflen); + + for(argc=0;argc < numstrings && argc < MAX_PARAM_STRINGS - 1; ++argc) + argv[argc] = + (argc) ? argv[argc - 1] + strlen(argv[argc - 1]) + 1 : strings; + + argv[argc] = NULL; + + if((elibrary = LoadLibraryEx(mf.event, NULL, DONT_RESOLVE_DLL_REFERENCES)) + == NULL) + FM_RETURN(NULL); + + if(!FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | + FORMAT_MESSAGE_IGNORE_INSERTS | NL_TREATMENT, + (LPCVOID) elibrary, + id, + DEFAULT_LANGID, + tmpbuff2, + (DWORD) bufflen, + NULL)){ + FM_RETURN(NULL); + } + + if(mf.param != NULL) + plibrary = LoadLibraryEx(mf.param, NULL, DONT_RESOLVE_DLL_REFERENCES); + + if(plibrary){ + for(i=0;argv[i];++i) + if(expand_message(argv[i], plibrary, tmpbuff, bufflen) != NULL) + argv[i] = strdup(tmpbuff); + else + argv[i] = strdup(argv[i]); /* All gets malloced, so I don't have to + bother what to free... */ + if(expand_message(tmpbuff2, plibrary, tmpbuff, bufflen) != NULL) + strcpy(tmpbuff2,tmpbuff); + } + + if(!FormatMessage(FORMAT_MESSAGE_FROM_STRING | + FORMAT_MESSAGE_ARGUMENT_ARRAY | NL_TREATMENT, + (LPCVOID) tmpbuff2, + id, + DEFAULT_LANGID, + buff, + (DWORD) bufflen, + argv)){ + FM_RETURN(NULL); + } + + FM_RETURN(buff); + +} +#undef FM_RETURN diff --git a/lib/os_mon/c_src/nteventlog/elog_format.h b/lib/os_mon/c_src/nteventlog/elog_format.h new file mode 100644 index 0000000000..3fb19367ab --- /dev/null +++ b/lib/os_mon/c_src/nteventlog/elog_format.h @@ -0,0 +1,40 @@ +/* + * %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% + */ +#ifndef _ELOG_FORMAT_H +#define _ELOG_FORMAT_H +/* + * Module: elog_format + * Purpouse: Format messages in the eventlog. + * ToDo: Maximum buffersize is used... + */ + +#include "elog_global.h" + +char *format_message(MessageFiles mf, DWORD id, + char *strings, int numstrings, + char *buff, int bufflen); +/* + * Formats an eventlog message with the messagefiles + * in mf, the ID id, the stringarray strings, + * containing numstrings strings into buff. + * if bufflen is to small or anything else failes, + * the return value is NULL. + */ + +#endif /* _ELOG_FORMAT_H */ diff --git a/lib/os_mon/c_src/nteventlog/elog_global.h b/lib/os_mon/c_src/nteventlog/elog_global.h new file mode 100644 index 0000000000..f992b7184f --- /dev/null +++ b/lib/os_mon/c_src/nteventlog/elog_global.h @@ -0,0 +1,57 @@ +/* + * %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% + */ +#ifndef _ELOG_GLOBAL_H +#define _ELOG_GLOBAL_H +#include +#include +#include +#include +#include + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include "windows.h" + +#ifdef _DEBUG +/*#define HARDDEBUG*/ +#define DEBUG +#endif + +/* + * Compile time limits + */ +#define SMALLBUFSIZ 512 +#define BIGBUFSIZ 2048 +#define MAX_PARAM_STRINGS 200 +#define MAX_FACILITY_NAME 100 + +/* + * Structure containing message file names + */ +typedef struct _message_files { + char *event; + char *param; +} MessageFiles; + +/* + * How to get the default language + */ +#define DEFAULT_LANGID MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) + +#endif /* _ELOG_GLOBAL_H */ 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; +} diff --git a/lib/os_mon/c_src/nteventlog/elog_pipe_stdin.c b/lib/os_mon/c_src/nteventlog/elog_pipe_stdin.c new file mode 100644 index 0000000000..c333c455a3 --- /dev/null +++ b/lib/os_mon/c_src/nteventlog/elog_pipe_stdin.c @@ -0,0 +1,151 @@ +/* + * %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% + */ +#include "elog_global.h" +#include "elog_pipe_stdin.h" + +/* + * Data for the handling of a pipe stdin, + * data is read in a separate thread, so locking and + * event signaling needs to be done. + */ + +static CRITICAL_SECTION io_crit; +static char *stdin_buff = NULL; +static int stdin_siz = 0; +static int stdin_len = 0; +static int stdin_eof = 0; +/* end syncronized objects */ +static int stdin_is_console = 0; +static HANDLE stdin_event; + +DWORD WINAPI stdin_thread(LPVOID ptr){ + HANDLE in = GetStdHandle(STD_INPUT_HANDLE); + char buff[1]; + DWORD red; + for(;;){ + if(!ReadFile(in, buff, (DWORD) 1, &red, NULL)){ + if(GetLastError() == ERROR_BROKEN_PIPE){ + EnterCriticalSection(&io_crit); + stdin_eof = 1; + SetEvent(stdin_event); + LeaveCriticalSection(&io_crit); + return 0; + } + return 1; + }else if(red == 0){ + EnterCriticalSection(&io_crit); + stdin_eof = 1; + SetEvent(stdin_event); + LeaveCriticalSection(&io_crit); + return 0; + } +#ifdef HARDDEBUG + fprintf(stderr,"stdin_thread go data (%d)\n",(int)*buff); +#endif + EnterCriticalSection(&io_crit); + if(stdin_len + 1 >= stdin_siz){ + if(!stdin_siz) + stdin_buff = malloc(stdin_siz = 100); + else + stdin_buff = realloc(stdin_buff, stdin_siz +=100); + } + stdin_buff[stdin_len++] = *buff; + SetEvent(stdin_event); + LeaveCriticalSection(&io_crit); + } + return 0; +} + +BOOL peek_pipe_stdin_eof(void){ + BOOL ret; + EnterCriticalSection(&io_crit); + if((ret = !!stdin_eof)) + ResetEvent(stdin_event); /* Now we "unsignal" */ + LeaveCriticalSection(&io_crit); + return ret; +} + +int read_pipe_stdin(char *buff, int max){ + int ret; + EnterCriticalSection(&io_crit); + if(stdin_len == 0){ + if(!stdin_eof){ + LeaveCriticalSection(&io_crit); + WaitForSingleObject(stdin_event,INFINITE); + EnterCriticalSection(&io_crit); + if(!stdin_len){ + if(stdin_eof){ + /* Stay signaled */ + LeaveCriticalSection(&io_crit); + return 0; + } else { + ResetEvent(stdin_event); + LeaveCriticalSection(&io_crit); + return -1; + } + } + } else { + /* Stay signaled */ + LeaveCriticalSection(&io_crit); + return 0; + } + } +#ifdef HARDDEBUG + fprintf(stderr,"read_pipe_stdin got data.\n" + "max = %d, stdin_len = %d, *stdin_buff = %d\n", + max,stdin_len,*stdin_buff); +#endif + /* stdin_len should be something now */ + if(stdin_len > max){ + memcpy(buff,stdin_buff,max); + memmove(stdin_buff,stdin_buff + max,stdin_len - max); + stdin_len -= max; + ret = max; + } else { + memcpy(buff,stdin_buff,stdin_len); + ret = stdin_len; + stdin_len = 0; + } + if(!stdin_eof) /* Stay signaled if EOF */ + ResetEvent(stdin_event); + LeaveCriticalSection(&io_crit); + return ret; +} + +BOOL setup_pipe_stdin(void){ + HANDLE in = GetStdHandle(STD_INPUT_HANDLE); + DWORD dummy; + if(GetConsoleMode(in, &dummy)){ + stdin_is_console = 1; + stdin_event = in; + return TRUE; + } + stdin_event = CreateEvent(NULL, TRUE, FALSE, NULL); + InitializeCriticalSection(&io_crit); + return (_beginthreadex(NULL,0,&stdin_thread,NULL,0,&dummy)); +} + +BOOL console_stdin(void){ + return stdin_is_console; +} + +HANDLE get_stdin_event(void){ + return stdin_event; +} + diff --git a/lib/os_mon/c_src/nteventlog/elog_pipe_stdin.h b/lib/os_mon/c_src/nteventlog/elog_pipe_stdin.h new file mode 100644 index 0000000000..a9a91b685f --- /dev/null +++ b/lib/os_mon/c_src/nteventlog/elog_pipe_stdin.h @@ -0,0 +1,90 @@ +/* + * %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% + */ +#ifndef _ELOG_PIPE_STDIN_H +#define _ELOG_PIPE_STDIN_H +/* + * Module: elog_pipe_stdin + * Purpouse: Read data from stdin when stdin is a pipe + * and deliver events only when data is really availabel or + * end of file is reached. + * If we would wait on an ordinary pipe handle, we + * would return immediately as it's always "signaled". + * some kind of asyncronous I/O in the win32 way is + * not possible as it's not supported on anonymous pipes + * (besides we have not opened the file ourselves so we + * cannot specify that we want async I/O...). + * ToDo: The reading is inneficcient, the buffering + * goes on forever, which would be dangerous if to much + * data was passed into stdin. The handling of + * Console stdin should be transparent instead of + * forcing the user of the module to check if this is a + * console for selecting between ReadFile and + * read_pipe_stdin. + * The handling of the event object is somewhat strange + * because I want to know about EOF before I've read + * to it. + */ + +BOOL peek_pipe_stdin_eof(void); +/* + * Returns TRUE if eof is reached, regardless of + * if there still is unread data in the buffer. + * Should not be called if console_stdin() returns TRUE. + * Resets the event object if it returns TRUE. + */ + +int read_pipe_stdin(char *buff, int max); +/* + * Reads from stdin, minimum 1 byte and + * maximum max bytes into buff. If EOF + * is reached and no bytes were read, + * the return value is 0. + * Should not be called if console_stdin() returns TRUE. + * The event object for stdin will get unsignaled if + * end of file is not reached (if peek_pipe_stdin_eof() + * would return false). + */ + +BOOL setup_pipe_stdin(void); +/* + * Initializes the module, returns TRUE if OK. + * If stdin is a console, no thread is created + * and the event objet returned by get_Stdin_event + * will be the console handle. + * Check if stdin was a console with the console_stdin() + * function. + */ + +BOOL console_stdin(void); +/* + * Returns true if stdin was a console, in which case + * normal Win32 console I/O functions have to + * be used. + * get_stdin_event() will return the console handle, + * which is signalled whenever an event reaches + * the console window (like mouse events etc). + */ + +HANDLE get_stdin_event(void); +/* + * Returns a event handle that can be waited upon with + * WaitForSingleObject and friends. It is possibly a console + * handle, see console_stdin(). + */ +#endif /* _ELOG_PIPE_STDIN_H */ diff --git a/lib/os_mon/c_src/nteventlog/elog_registry.c b/lib/os_mon/c_src/nteventlog/elog_registry.c new file mode 100644 index 0000000000..478db1e56b --- /dev/null +++ b/lib/os_mon/c_src/nteventlog/elog_registry.c @@ -0,0 +1,295 @@ +/* + * %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% + */ +#include "elog_global.h" +#include "elog_util.h" +#include "elog_registry.h" + +/* + * Constants for get/set_regkeys + */ +#define APP_ROOT_KEY "SOFTWARE\\Ericsson\\Erlang" +#define APP_SUB_KEY "EventLogReader" +#define APP_VERSION "1.0" +#define LATEST_RECORD_NAME "LatestRecord" +#define LATEST_TIME_NAME "LatestTime" +/* + * Constants for get_messagefiles + */ +#define LOG_KEY_TMPL "SYSTEM\\CurrentControlSet\\Services\\EventLog\\%s\\%s" +#define EVENT_MSG_FILE_NAME "EventMessageFile" +#define PARAM_MSG_FILE_NAME "ParameterMessageFile" +MessageFiles get_messagefiles(const char *category, const char *facility, + char *eventbuff, int eventbufflen, + char *parambuff, int parambufflen){ + char *b1 = malloc(strlen(LOG_KEY_TMPL)+strlen(category)+strlen(facility)+1); + HKEY key; + char name[1024]; + char val[MAX_PATH]; + MessageFiles mf = { NULL, NULL }; + DWORD namelen, vallen, type, i, ret; + + if(!b1) + return mf; + sprintf(b1,LOG_KEY_TMPL,category,facility); + if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, b1, 0, KEY_QUERY_VALUE, &key) != + ERROR_SUCCESS){ + free(b1); + return mf; + } + free(b1); + for(namelen=1024,vallen=MAX_PATH,i=0; + ERROR_SUCCESS == RegEnumValue(key, + i, + name, + &namelen, + NULL, + &type, + (LPBYTE) val, + &vallen); + namelen=1024,vallen=MAX_PATH,++i){ + if(!strcmp(name,EVENT_MSG_FILE_NAME)){ + if(type == REG_EXPAND_SZ){ + ret = ExpandEnvironmentStrings(val,eventbuff,eventbufflen); + if(((int) ret) > eventbufflen || !ret) + break; + } else { + if(((int) strlen(val)) >= eventbufflen) + break; + else + strcpy(eventbuff,val); + } + mf.event = eventbuff; + } else if(!strcmp(name,PARAM_MSG_FILE_NAME)){ + if(type == REG_EXPAND_SZ){ + ret = ExpandEnvironmentStrings(val,parambuff,parambufflen); + if(((int) ret) > parambufflen || !ret) + break; + } else { + if(((int) strlen(val)) >= parambufflen) + break; + else + strcpy(parambuff,val); + } + mf.param = parambuff; + } + } + RegCloseKey(key); + return mf; +} + + +int create_regkeys(char *identifier){ + HKEY key,key2; + DWORD dispositions; + int i,j; + char *values[] = { + LATEST_RECORD_NAME, + LATEST_TIME_NAME, + NULL + }; + + DWORD zero = 0; + + if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, + APP_ROOT_KEY "\\" APP_SUB_KEY "\\" + APP_VERSION, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_CREATE_SUB_KEY, + NULL, + &key, + &dispositions) != ERROR_SUCCESS){ + return -1; + } + if(RegCreateKeyEx(key, + identifier, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_CREATE_SUB_KEY, + NULL, + &key2, + &dispositions)){ + RegCloseKey(key); + return -1; + } + RegCloseKey(key); + for(i=0; category_tab[i] != NULL; ++i){ + if(RegCreateKeyEx(key2, + category_tab[i], + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, + NULL, + &key, + &dispositions) != ERROR_SUCCESS){ + RegCloseKey(key2); + return -1; + } + for(j=0; values[j] != NULL; ++j){ + if(RegSetValueEx(key, + values[j], + 0, + REG_DWORD, + (BYTE *) &zero, + sizeof(DWORD)) != ERROR_SUCCESS){ + RegCloseKey(key); + RegCloseKey(key2); + return -1; + } + } + RegCloseKey(key); + } + RegCloseKey(key2); + return 0; +} + + +int set_regkeys(char *identifier, RegKeys *keys, int num_keys){ + HKEY key; + char knbuff[SMALLBUFSIZ]; + int i; + for(i=0; i