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/Makefile | 5 + lib/os_mon/c_src/Makefile.in | 144 ++++++ lib/os_mon/c_src/cpu_sup.c | 455 ++++++++++++++++++ lib/os_mon/c_src/ferrule.c | 160 +++++++ lib/os_mon/c_src/memsup.c | 655 ++++++++++++++++++++++++++ lib/os_mon/c_src/memsup.h | 46 ++ lib/os_mon/c_src/mod_syslog.c | 137 ++++++ 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 ++++ lib/os_mon/c_src/win32sysinfo.c | 318 +++++++++++++ 18 files changed, 3449 insertions(+) create mode 100644 lib/os_mon/c_src/Makefile create mode 100644 lib/os_mon/c_src/Makefile.in create mode 100644 lib/os_mon/c_src/cpu_sup.c create mode 100644 lib/os_mon/c_src/ferrule.c create mode 100644 lib/os_mon/c_src/memsup.c create mode 100644 lib/os_mon/c_src/memsup.h create mode 100644 lib/os_mon/c_src/mod_syslog.c 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 create mode 100644 lib/os_mon/c_src/win32sysinfo.c (limited to 'lib/os_mon/c_src') diff --git a/lib/os_mon/c_src/Makefile b/lib/os_mon/c_src/Makefile new file mode 100644 index 0000000000..a34434a7e6 --- /dev/null +++ b/lib/os_mon/c_src/Makefile @@ -0,0 +1,5 @@ +# +# Invoke with GNU make or clearmake -C gnu. +# + +include $(ERL_TOP)/make/run_make.mk diff --git a/lib/os_mon/c_src/Makefile.in b/lib/os_mon/c_src/Makefile.in new file mode 100644 index 0000000000..1a371eb380 --- /dev/null +++ b/lib/os_mon/c_src/Makefile.in @@ -0,0 +1,144 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-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 $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +CC = @CC@ +LD = @LD@ +LIBS = @LIBS@ +CPU_SUP_LIBS = @CPU_SUP_LIBS@ + +BINDIR = ../priv/bin/$(TARGET) +OBJDIR = ../priv/obj/$(TARGET) + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../vsn.mk +VSN=$(OS_MON_VSN) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/os_mon-$(VSN) + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- +ifeq ($(findstring win32,$(TARGET)),win32) +PROGRAMS = \ + win32sysinfo.exe \ + nteventlog.exe +C_FILES=win32sysinfo.c \ + nteventlog/elog_main.c \ + nteventlog/elog_util.c \ + nteventlog/elog_registry.c \ + nteventlog/elog_pipe_stdin.c \ + nteventlog/elog_format.c + +EVLOG_OBJECTS = \ + $(OBJDIR)/elog_main.o \ + $(OBJDIR)/elog_util.o \ + $(OBJDIR)/elog_registry.o \ + $(OBJDIR)/elog_pipe_stdin.o \ + $(OBJDIR)/elog_format.o + +ENTRY_OBJ=$(ERL_TOP)/erts/obj/$(TARGET)/port_entry.o +PORT_ENTRY_POINT=erl_port_entry +ENTRY_LDFLAGS=-entry:$(PORT_ENTRY_POINT) +else +ifeq ($(findstring vxworks_simso,$(TARGET)),vxworks_simso) +PROGRAMS = +else +PROGRAMS = \ + memsup @os_mon_programs@ +C_FILES= $(PROGRAMS:%=%.c) +endif +endif + +TARGET_FILES= $(PROGRAMS:%=$(BINDIR)/%) + +LDFLAGS = @LDFLAGS@ + +ALL_CFLAGS = @CFLAGS@ @DEFS@ $(CFLAGS) + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +debug opt: $(OBJDIR) $(BINDIR) $(TARGET_FILES) + +$(OBJDIR): + -@mkdir -p $(OBJDIR) + +$(BINDIR): + -@mkdir -p $(BINDIR) + +clean: + rm -f $(TARGET_FILES) + rm -f core *~ + +docs: + +# ---------------------------------------------------- +# Special Build Targets +# ---------------------------------------------------- + +$(BINDIR)/win32sysinfo.exe: $(OBJDIR)/win32sysinfo.o $(ENTRY_OBJ) + $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/win32sysinfo.o $(ENTRY_OBJ) + +$(BINDIR)/nteventlog.exe: $(EVLOG_OBJECTS) + $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(EVLOG_OBJECTS) $(ENTRY_OBJ) + +$(BINDIR)/ferrule: $(OBJDIR)/ferrule.o + $(LD) $(LDFLAGS) -o $@ $< + +$(BINDIR)/mod_syslog: $(OBJDIR)/mod_syslog.o + $(LD) $(LDFLAGS) -o $@ $< + +$(BINDIR)/memsup: $(OBJDIR)/memsup.o + $(LD) $(LDFLAGS) -o $@ $< + +$(BINDIR)/cpu_sup: $(OBJDIR)/cpu_sup.o + $(LD) $(LDFLAGS) -o $@ $< $(CPU_SUP_LIBS) + +$(OBJDIR)/%.o: %.c + $(CC) -c -o $@ $(ALL_CFLAGS) $< + +$(OBJDIR)/%.o: nteventlog/%.c + $(CC) -c -o $@ $(ALL_CFLAGS) $< + +$(OBJDIR)/memsup.o: memsup.h + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +ifeq ($(findstring vxworks_simso,$(TARGET)),vxworks_simso) +release_spec: +else +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DATA) $(C_FILES) $(RELSYSDIR)/src + $(INSTALL_DIR) $(RELSYSDIR)/priv/bin + $(INSTALL_PROGRAM) $(TARGET_FILES) $(RELSYSDIR)/priv/bin +endif + +release_docs_spec: diff --git a/lib/os_mon/c_src/cpu_sup.c b/lib/os_mon/c_src/cpu_sup.c new file mode 100644 index 0000000000..fbf318c614 --- /dev/null +++ b/lib/os_mon/c_src/cpu_sup.c @@ -0,0 +1,455 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-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% + */ +/* + * CPU supervision + * + * Uses kstat library only available on Solaris 2 + * Compile with: gcc -o cpu_sup cpu_sup.c -lkstat + * + * Use open_port({spawn,Prog},[stream]) to communicate. + * + */ + +#include +#include +#include + +#if defined(__sun__) +#include +#endif + +#include +#include + +#if defined(__linux__) +#include /* strlen */ + +#define PROCSTAT "/proc/stat" +#define BUFFERSIZE (256) +typedef struct { + unsigned int id; + unsigned long long + /* total, */ + user, + nice_user, + kernel, + idle, + io_wait, + hard_irq, + soft_irq, + steal; +} cpu_t; + +#endif + +#define FD_IN (0) +#define FD_OUT (1) +#define FD_ERR (2) + +#define PING 'p' +#define NPROCS 'n' +#define AVG1 '1' +#define AVG5 '5' +#define AVG15 'f' +#define UTIL 'u' +#define QUIT 'q' + + +#define CU_CPU_ID (0) +#define CU_USER (1) +#define CU_NICE_USER (2) +#define CU_KERNEL (3) +#define CU_IO_WAIT (4) +#define CU_IDLE (5) +#define CU_HARD_IRQ (6) +#define CU_SOFT_IRQ (7) +#define CU_STEAL (8) + +#define CU_VALUES (9) +#define CU_KSTAT_VALUES (5) + +/* +#define CU_FLG_CPU_ID (0 << 1) +#define CU_FLG_USER (0 << 2) +#define CU_FLG_NICE_USER (0 << 3) +#define CU_FLG_KERNEL (0 << 4) +#define CU_FLG_IO_WAIT (0 << 5) +#define CU_FLG_IDLE (0 << 6) +#define CU_FLG_HARD_IRQ (0 << 7) +#define CU_FLG_SOFT_IRQ (0 << 8) +#define CU_FLG_STEAL (0 << 9) +*/ + +/* util_measure + * In: + * unsigned int **result_vec + * int *result_sz + * Purpose: + * Retrieve CPU utilization + * result_vec has 2 + np*ne*2 entries where np is number_of_cpus + * |------|------| + * | np | ne | + * |------|------| + * |val_id| value| (One entry) + * |------|------| + * |val_id| value| (One entry) + * |------|------| + * ...... + * |------|------| + * |val_id| value| + * |------|------| + * np = number of processors + * ne = number of entries per processor + */ + +static void util_measure(unsigned int **result_vec, int *result_sz); + +static unsigned int misc_measure(char* name); +static void send(unsigned int data); +static void sendv(unsigned int data[], int ints); +static void error(char* err_msg); + +#if defined(__sun__) +static kstat_ctl_t *kstat_ctl; +#endif + +#if defined(__linux__) +static int processors_online() { + return (int)sysconf(_SC_NPROCESSORS_ONLN); +} +#endif + +int main(int argc, char** argv) { + char cmd; + int rc; + int sz; + unsigned int *rv; + unsigned int no_of_cpus = 0; + +#if defined(__sun__) + kstat_ctl = kstat_open(); + if(!kstat_ctl) + error("Can't open header kstat"); +#endif + +#if defined(__linux__) + no_of_cpus = processors_online(); + if ( (rv = (unsigned int*)malloc(sizeof(unsigned int)*(2 + 2*no_of_cpus*CU_VALUES))) == NULL) { + error("cpu_cup: malloc error"); + } +#endif + + while(1) { + + rc = read(FD_IN, &cmd, 1); + if (rc < 0) { + if (errno == EINTR) + continue; + error("Error reading from Erlang"); + } + + if(rc == 0) + error("Erlang has closed"); + + switch(cmd) { + case PING: send(4711); break; +#if defined(__sun__) + case NPROCS: send(misc_measure("nproc")); break; + case AVG1: send(misc_measure("avenrun_1min")); break; + case AVG5: send(misc_measure("avenrun_5min")); break; + case AVG15: send(misc_measure("avenrun_15min")); break; +#endif + case UTIL: util_measure(&rv,&sz); sendv(rv, sz); break; + case QUIT: free((void*)rv); return 0; + default: error("Bad command"); break; + } + } + return 0; /* supress warnings */ +} +/* ---------------------------- * + * Linux stat functions * + * ---------------------------- */ + +#if defined(__linux__) + +static cpu_t *read_procstat(FILE *fp, cpu_t *cpu) { + char buffer[BUFFERSIZE]; + + fgets(buffer, BUFFERSIZE, fp); + sscanf(buffer, "cpu%u %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", + &(cpu->id), + &(cpu->user), + &(cpu->nice_user), + &(cpu->kernel), + &(cpu->idle), + &(cpu->io_wait), + &(cpu->hard_irq), + &(cpu->soft_irq), + &(cpu->steal)); + + return cpu; +} + +static void util_measure(unsigned int **result_vec, int *result_sz) { + int no_of_cpus = processors_online(); + int i; + char buffer[BUFFERSIZE]; + FILE *fp; + unsigned int *rv = NULL; + cpu_t cpu; + + if ( (fp = fopen(PROCSTAT,"r")) == NULL) { + /* Check if procfs is mounted, + * otherwise: + * try and try again, bad procsfs. + */ + *result_sz = 0; + return; + } + + fgets(buffer, BUFFERSIZE, fp); /*ignore read*/ + rv = *result_vec; + rv[0] = no_of_cpus; + rv[1] = CU_VALUES; + ++rv; /* first value is number of cpus */ + ++rv; /* second value is number of entries */ + + for (i = 0; i < no_of_cpus; ++i) { + read_procstat(fp, &cpu); + + rv[ 0] = CU_CPU_ID; rv[ 1] = cpu.id; + rv[ 2] = CU_USER; rv[ 3] = cpu.user; + rv[ 4] = CU_NICE_USER; rv[ 5] = cpu.nice_user; + rv[ 6] = CU_KERNEL; rv[ 7] = cpu.kernel; + rv[ 8] = CU_IO_WAIT; rv[ 9] = cpu.io_wait; + rv[10] = CU_IDLE; rv[11] = cpu.idle; + rv[12] = CU_HARD_IRQ; rv[13] = cpu.hard_irq; + rv[14] = CU_SOFT_IRQ; rv[15] = cpu.soft_irq; + rv[16] = CU_STEAL; rv[17] = cpu.steal; + rv += CU_VALUES*2; + } + + fclose(fp); + *result_sz = 2 + 2*CU_VALUES * no_of_cpus; +} + +#endif + +/* ---------------------------- * + * Unix kstat functions * + * ---------------------------- */ + +#if defined(__sun__) +static unsigned int misc_measure(char* name) { + static kstat_t *ksp = NULL; + kstat_named_t* entry; + kid_t kcid; + + kcid = kstat_chain_update(kstat_ctl); + + if(kcid == -1) + error("Error updating kstat chain"); + + if (!ksp || kcid != 0) { + + /* The kstat chain changed (or we are initializing); + find system misc entry in the new chain... */ + + ksp = kstat_lookup(kstat_ctl,"unix",0,"system_misc"); + if(!ksp) + error("Can't open system_misc kstat"); + } + + kstat_read(kstat_ctl,ksp,NULL); + entry = kstat_data_lookup(ksp,name); + if(!entry) + return -1; + + if(entry->data_type != KSTAT_DATA_ULONG) + return -1; + + return entry->value.ul; +} + + +static int cpu_cmp(const void *p1, const void *p2) { + kstat_t *ksp1 = *((kstat_t **) p1); + kstat_t *ksp2 = *((kstat_t **) p2); + + if (ksp1->ks_instance > ksp2->ks_instance) + return 1; + if (ksp1->ks_instance < ksp2->ks_instance) + return -1; + return 0; +} + + +static void util_measure(unsigned int **result_vec, int *result_sz) { + static int no_of_cpus = 0; + static kstat_t **cpu_ksps = NULL; + static unsigned int * resv = NULL; + unsigned int *rv = NULL; + kstat_t *ksp; + kid_t kcid; + int cpu_stats_read; + int i; + + kcid = kstat_chain_update(kstat_ctl); + + if(kcid == -1) + error("Error updating kstat chain"); + + if (no_of_cpus == 0 || kcid != 0) { + + /* The kstat chain changed (or we are initializing); + find cpu_stat entries in the new chain... */ + + no_of_cpus = 0; + + for(ksp = kstat_ctl->kc_chain; ksp; ksp = ksp->ks_next) { + if (strcmp(ksp->ks_module, "cpu_stat") == 0 + && ksp->ks_type == KSTAT_TYPE_RAW) { + no_of_cpus++; + /* Assumes that modifications of the cpu_stat_t struct + in future releases of Solaris only are additions + of fields at the end of the struct. */ + if(ksp->ks_data_size < sizeof(cpu_stat_t)) + error("Error: unexpected kstat data size"); + } + } + + free((void *) cpu_ksps); + if (no_of_cpus > 0) { + cpu_ksps = (kstat_t **) malloc(no_of_cpus*sizeof(kstat_t *)); + if(!cpu_ksps) + error("Error allocating memory"); + + i = 0; + for(ksp = kstat_ctl->kc_chain; + ksp && i < no_of_cpus; + ksp = ksp->ks_next) { + if (strcmp(ksp->ks_module, "cpu_stat") == 0 + && ksp->ks_type == KSTAT_TYPE_RAW) { + cpu_ksps[i++] = ksp; + } + } + + if (i != no_of_cpus) + error("Error: private kstat chain copy unexpectedly changed"); + + /* Erlang assumes that cpu information are sent in ascending order; + sort them ... */ + qsort((void *)cpu_ksps,(size_t)no_of_cpus,sizeof(kstat_t *),cpu_cmp); + + } + + free((void *) resv); +/* kstat defined values are: + * CU_CPU_ID + * CU_USER + * CU_KERNEL + * CU_IO_WAIT + * CU_IDLE + */ + resv = (unsigned int *) malloc(sizeof(unsigned int)*(2 + 2*no_of_cpus*CU_KSTAT_VALUES)); + if(!resv) + error("Error allocating memory"); + + } + + /* Read cpu utilization statistics ... */ + + rv = resv; + rv++; /*first entry is np*/ + rv++; /*second entry is ne*/ + cpu_stats_read = 0; + + for(i = 0; i < no_of_cpus; i++) { + if (kstat_read(kstat_ctl, cpu_ksps[i], NULL) != -1) { + cpu_stat_t *cpu_stat = (cpu_stat_t *)cpu_ksps[i]->ks_data; + + rv[ 0] = CU_CPU_ID; rv[ 1] = cpu_ksps[i]->ks_instance; + rv[ 2] = CU_USER; rv[ 3] = cpu_stat->cpu_sysinfo.cpu[CPU_USER]; + rv[ 4] = CU_KERNEL; rv[ 5] = cpu_stat->cpu_sysinfo.cpu[CPU_KERNEL]; + rv[ 6] = CU_IO_WAIT; rv[ 7] = cpu_stat->cpu_sysinfo.cpu[CPU_WAIT]; + rv[ 8] = CU_IDLE; rv[ 9] = cpu_stat->cpu_sysinfo.cpu[CPU_IDLE]; + + rv += CU_KSTAT_VALUES*2; + cpu_stats_read++; + } + } + + resv[0] = cpu_stats_read; + resv[1] = CU_KSTAT_VALUES; + + *result_vec = resv; + *result_sz = 2 + 2* CU_KSTAT_VALUES * cpu_stats_read; + +} +#endif + +/* ---------------------------- * + * Generic functions * + * ---------------------------- */ + +static void send(unsigned int data) { sendv(&data, 1); } + +static void sendv(unsigned int data[], int ints) { + static unsigned char *buf = NULL; + static int bufsz = 0; + int rc, di, bi, msgsz; + + /* Assumes 32-bit integers... */ + + msgsz = 4*ints; + + if(bufsz < msgsz) { + if (buf != NULL) free((void *) buf); + buf = malloc(msgsz); + if (!buf) error("Error allocating memory"); + bufsz = msgsz; + } + + for(bi = 0, di = 0; di < ints; di++) { + buf[bi++] = (data[di] >> 24) & 0xff; + buf[bi++] = (data[di] >> 16) & 0xff; + buf[bi++] = (data[di] >> 8) & 0xff; + buf[bi++] = (data[di] ) & 0xff; + } + + bi = 0; + do { + rc = write(FD_OUT, &buf[bi], msgsz - bi); + if (rc < 0) { + if (errno == EINTR) + continue; + error("Error writing to Erlang"); + } + bi += rc; + } while(msgsz - bi > 0); + +} + +static void error(char* err_msg) { + write(FD_ERR, err_msg, strlen(err_msg)); + write(FD_ERR, "\n", 1); + exit(-1); +} + + diff --git a/lib/os_mon/c_src/ferrule.c b/lib/os_mon/c_src/ferrule.c new file mode 100644 index 0000000000..744f302b2d --- /dev/null +++ b/lib/os_mon/c_src/ferrule.c @@ -0,0 +1,160 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-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 +#include +#include +#include +#include +#include +#include +#include +#include + + /* arguments */ +#define OWNPATH 1 +#define NARG 1 + + /* status codes */ +#define OK 0 +#define NOT_OK 1 + + /* hardwired constants*/ +#define PIPENAME "syslog.otp" +#define DIE_CMD "die" +#define ONLY_STDIN_CMD "only_stdin" +#define BUFFER_SIZE 1001 +#define MAXPATH_SIZE 1001 +#define STDIN 0 +#define STDOUT 1 +#define HEADER_SIZE 2 +#define WAIT 10 +#define INTERVAL 1 +#define FALSE 0 +#define TRUE 1 +#define FDS_STDIN 0 +#define FDS_PIPE 1 + +main(int argc, char *argv[]) +/* usage: ferrule ownpath */ +{ + int i, pipe_fd; + long int nfds=2L; + char buf[BUFFER_SIZE], pipename[MAXPATH_SIZE], packet_size[2]; + struct pollfd fds[2]; + struct stat stat_buf; + + /* enough arguments? */ + if(argc < NARG+1) + exit(NOT_OK); + + /* make pipe name */ + strcpy(pipename, argv[OWNPATH]); + strcat(pipename, "/"); + strcat(pipename, PIPENAME); + + /* wait for creation of pipe */ + for(i=WAIT; i>0; i--) + if(stat(pipename, &stat_buf) == 0) + break; + else{ + if(i == 0) + exit(NOT_OK); + else + sleep(INTERVAL); + } + + /* open pipe, exit if error */ + if((pipe_fd = open(pipename, O_RDONLY | O_NONBLOCK)) == -1) + exit(NOT_OK); + + /* setup for pipe */ + fds[FDS_PIPE].fd = pipe_fd; + fds[FDS_PIPE].events = POLLRDNORM; + fds[FDS_PIPE].revents = 0; + + /* setup for stdin */ + fds[FDS_STDIN].fd = STDIN; + fds[FDS_STDIN].events = POLLRDNORM; + fds[FDS_STDIN].revents = 0; + + /* loop */ + while(1){ + /* wait for input */ + if(poll(&fds[0], nfds, INFTIM) == -1) + exit(NOT_OK); + + /* analyse input from pipe */ + if(fds[FDS_PIPE].revents != 0){ + + /* pipe error, error exit */ + if((fds[FDS_PIPE].revents & (POLLHUP | POLLERR | POLLNVAL)) != 0) + exit(NOT_OK); + + /* read pipe and write to stdout, exit if error */ + if((fds[FDS_PIPE].revents & POLLRDNORM) != 0){ + i=0; + do{ + read(pipe_fd, &buf[i++], (size_t)1); + }while(buf[i-1] != '\n'); + i--; + + /* send if string is not empty */ + if(i != 0){ + /* make packet size, [0]=MSB, [1]=LSB */ + packet_size[0] = (i >> 8) & 0xff; + packet_size[1] = i & 0xff; + + /* send to OTP process */ + if(write(STDOUT, packet_size, HEADER_SIZE) != HEADER_SIZE) + exit(NOT_OK); + if(write(STDOUT, buf, i) != i) + exit(NOT_OK); + } + } + } + + /* analyse input from stdin */ + if(fds[FDS_STDIN].revents != 0){ + + /* error exit */ + if((fds[FDS_STDIN].revents & (POLLHUP | POLLERR | POLLNVAL)) != 0) + exit(NOT_OK); + + /* read bytes */ + if((fds[FDS_STDIN].revents & POLLRDNORM) != 0){ + /* get packet size, [0]=MSB, [1]=LSB */ + if(read(STDIN, packet_size, HEADER_SIZE) != HEADER_SIZE) + exit(NOT_OK); + i = (packet_size[0] << 8) | packet_size[1]; + + /* get packet */ + if(read(STDIN, buf, i) != i) + exit(NOT_OK); + + /* check if die command */ + if(strncmp(DIE_CMD, buf, i) == FALSE) + exit(OK); + + /* check if only_stdin command */ + if(strncmp(ONLY_STDIN_CMD, buf, i) == FALSE) + nfds=1L; + } + } + } +} diff --git a/lib/os_mon/c_src/memsup.c b/lib/os_mon/c_src/memsup.c new file mode 100644 index 0000000000..241e7718db --- /dev/null +++ b/lib/os_mon/c_src/memsup.c @@ -0,0 +1,655 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-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% + */ + +/* + * Purpose: Portprogram for supervision of memory usage. + * + * Synopsis: memsup + * + * PURPOSE OF THIS PROGRAM + * + * This program supervises the memory status of the entire system, and + * sends status reports upon request from the Erlang system + * + * SPAWNING FROM ERLANG + * + * This program is started from Erlang as follows, + * + * Port = open_port({spawn, 'memsup'}, [{packet,1}]) for UNIX and VxWorks + * + * Erlang sends one of the request condes defined in memsup.h and this program + * answers in one of two ways: + * * If the request is for simple memory data (which is used periodically + * for monitoring) the answer is simply sent in two packets. + * * If the request is for the system specific data, the answer is delivered + * in two packets per value, first a tag value, then the actual + * value. The values are delivered "as is", this interface is + * mainly for VxWorks. + * All numbers are sent as strings of hexadecimal digits. + * + * SUNOS FAKING + * + * When using SunOS 4, the memory report is faked. The total physical memory + * is always reported to be 256MB, and the used fraction to be 128MB. + * + * If capabilities, such as sysconf or procfs, is not defined on the system + * memsup will fake memory usage as well. + * + * Following ordering is defined for extended memory, + * Linux: procfs -> sysinfo -> sysconf -> fake + * Sunos: sysconf -> fake + * other: arch specific + * + * Todo: + * Memory retrieval should be defined by capabilities and not by archs. + * Ordering should be defined arch. + * + * STANDARD INPUT, OUTPUT AND ERROR + * + * This program communicates with Erlang through the standard + * input and output file descriptors (0 and 1). These descriptors + * (and the standard error descriptor 2) must NOT be closed + * explicitely by this program at termination (in UNIX it is + * taken care of by the operating system itself; in VxWorks + * it is taken care of by the spawn driver part of the Emulator). + * + * END OF FILE + * + * If a read from a file descriptor returns zero (0), it means + * that there is no process at the other end of the connection + * having the connection open for writing (end-of-file). + * + * COMPILING + * + * When the target is VxWorks the identifier VXWORKS must be defined for + * the preprocessor (usually by a -D option). + */ + +#if defined(sgi) || defined(__sgi) || defined(__sgi__) +#include +#include +#endif + +#include +#include +#include + +#ifndef VXWORKS +#include +#endif + +#if (defined(__unix__) || defined(unix)) && !defined(USG) +#include +#endif + +#include + +#include +#include +#include + +#ifdef VXWORKS +#include +#include +#include +#endif + +#ifdef BSD4_4 +#include +#include +#if !defined (__OpenBSD__) && !defined (__NetBSD__) +#include +#endif +#if defined (__FreeBSD__) || defined(__DragonFly__) +#include +#endif +#endif + +#if defined (__linux__) +#include +#endif + +/* commands */ +#include "memsup.h" + +#define CMD_SIZE 1 +#define MAX_CMD_BUF 10 +#define ERLIN_FD 0 +#define ERLOUT_FD 1 + + +/* procfs */ +#if defined(__linux__) +#include +#define MEMINFO "/proc/meminfo" +#endif + +/* prototypes */ + +static void print_error(const char *,...); +#ifdef VXWORKS +extern int erl_mem_info_get(MEM_PART_STATS *); +#endif + +#ifdef VXWORKS +#define MAIN memsup + +static MEM_PART_STATS latest; +static unsigned long latest_system_total; /* does not fit in the struct */ + +#else +#define MAIN main +#endif + + +/* + * example, we want procfs information, now give them something equivalent: + * + * MemTotal: 4029352 kB old HighTotal + LowTotal + * MemFree: 1674168 kB old HighFree + LowFree + * MemShared: 0 kB old now always zero; not calculated + * Buffers: 417164 kB old temporary storage for raw disk blocks + * Cached: 371312 kB old in-memory cache for files read from the disk (the page cache) + + * Active: 1408492 kB new + + * Inact_dirty: 7772 kB new + * Inact_clean: 2008 kB new + * Inact_target: 0 kB new + * Inact_laundry: 0 kB new, and might be missing too + + * HighTotal: 0 kB + * HighFree: 0 kB memory area for userspace programs or for the pagecache + * LowTotal: 4029352 kB + * LowFree: 1674168 kB Highmem + kernel stuff, slab allocates here + + * SwapTotal: 4194296 kB old total amount of swap space available + * SwapFree: 4194092 kB old Memory which has been evicted from RAM + * Inactive: 549224 kB 2.5.41+ + * Dirty: 872 kB 2.5.41+ Memory which is waiting to get written back to the disk + * Writeback: 0 kB 2.5.41+ Memory which is actively being written back to the disk + * AnonPages: 787616 kB ?? + * Mapped: 113612 kB 2.5.41+ files which have been mmaped, such as libraries + * Slab: 342864 kB 2.5.41+ in-kernel data structures cache + * CommitLimit: 6208972 kB ?? + * Committed_AS: 1141444 kB 2.5.41+ + * PageTables: 9368 kB 2.5.41+ + * VmallocTotal: 34359738367 kB ?? total size of vmalloc memory area + * VmallocUsed: 57376 kB ?? amount of vmalloc area which is used + * VmallocChunk: 34359677947 kB ?? largest contigious block of vmalloc area which is free + * ReverseMaps: 5738 2.5.41+ number of rmap pte chains + * SwapCached: 0 kB 2.5.??+ + * HugePages_Total: 0 2.5.??+ + * HugePages_Free: 0 2.5.??+ + * HugePages_Rsvd: 0 2.5.??+ + * Hugepagesize: 2048 kB 2.5.?? + * + * This information should be generalized for generic platform i.e. erlang. + */ + + + +#define F_MEM_TOTAL (1 << 0) +#define F_MEM_FREE (1 << 1) +#define F_MEM_BUFFERS (1 << 2) +#define F_MEM_CACHED (1 << 3) +#define F_MEM_SHARED (1 << 4) +#define F_SWAP_TOTAL (1 << 5) +#define F_SWAP_FREE (1 << 6) + +typedef struct { + unsigned int flag; + unsigned long pagesize; + unsigned long total; + unsigned long free; + unsigned long buffered; + unsigned long cached; + unsigned long shared; + unsigned long total_swap; + unsigned long free_swap; +} memory_ext; + +typedef struct mem_table_struct { + const char *name; /* memory type name */ + unsigned long *slot; /* slot in return struct */ +} mem_table_struct; + + +/* static variables */ + +static char *program_name; + +static void +send(unsigned long value, unsigned long pagesize) { + char buf[32]; + int left, bytes, res; + int hex_zeroes; + + for (hex_zeroes = 0; (pagesize % 16) == 0; pagesize /= 16) { + hex_zeroes++; + } + + sprintf(buf+1, "%lx", value*pagesize); + bytes = strlen(buf+1); + while (hex_zeroes-- > 0) { + bytes++; + buf[bytes] = '0'; + } + buf[0] = (char) bytes; + left = ++bytes; + + while (left > 0) { + res = write(ERLOUT_FD, buf+bytes-left, left); + if (res <= 0){ + perror("Error writing to pipe"); + exit(1); + } + left -= res; + } +} + +static void +send_tag(int value){ + unsigned char buf[2]; + int res,left; + + buf[0] = 1U; + buf[1] = (unsigned char) value; + left = 2; + while(left > 0) { + if((res = write(ERLOUT_FD, buf+left-2,left)) <= 0){ + perror("Error writing to pipe"); + exit(1); + } else { + left -= res; + } + } +} + + +#ifdef VXWORKS +static void load_statistics(void){ + if(memPartInfoGet(memSysPartId,&latest) != OK) + memset(&latest,0,sizeof(latest)); + latest_system_total = latest.numBytesFree + latest.numBytesAlloc; + erl_mem_info_get(&latest); /* if it fails, latest is untouched */ +} +#endif + +#ifdef BSD4_4 +static int +get_vmtotal(struct vmtotal *vt) { + static int vmtotal_mib[] = {CTL_VM, VM_METER}; + size_t size = sizeof *vt; + + return sysctl(vmtotal_mib, 2, vt, &size, NULL, 0) != -1; +} +#endif + +#if defined(__linux__) + + +static int +get_mem_procfs(memory_ext *me){ + int fd, nread; + char buffer[4097]; + char *bp; + unsigned long value; + + me->flag = 0; + + if ( (fd = open(MEMINFO, O_RDONLY)) < 0) return -1; + + if ( (nread = read(fd, buffer, 4096)) < 0) { + close(fd); + return -1; + } + close(fd); + + buffer[nread] = '\0'; + + /* Total and free is NEEDED! */ + + bp = strstr(buffer, "MemTotal:"); + if (sscanf(bp, "MemTotal: %lu kB\n", &(me->total))) me->flag |= F_MEM_TOTAL; + + bp = strstr(buffer, "MemFree:"); + if (sscanf(bp, "MemFree: %lu kB\n", &(me->free))) me->flag |= F_MEM_FREE; + + /* Extensions */ + + bp = strstr(buffer, "Buffers:"); + if (sscanf(bp, "Buffers: %lu kB\n", &(me->buffered))) me->flag |= F_MEM_BUFFERS; + + bp = strstr(buffer, "Cached:"); + if (sscanf(bp, "Cached: %lu kB\n", &(me->cached))) me->flag |= F_MEM_CACHED; + + + /* Swap */ + + bp = strstr(buffer, "SwapTotal:"); + if (sscanf(bp, "SwapTotal: %lu kB\n", &(me->total_swap))) me->flag |= F_SWAP_TOTAL; + + bp = strstr(buffer, "SwapFree:"); + if (sscanf(bp, "SwapFree: %lu kB\n", &(me->free_swap))) me->flag |= F_SWAP_FREE; + + me->pagesize = 1024; /* procfs defines its size in kB */ + + return 1; +} +#endif + + +/* arch specific functions */ + +#if defined(VXWORKS) +static int +get_extended_mem_vxwork(memory_ext *me) { + load_statistics(); + me->total = (latest.numBytesFree + latest.numBytesAlloc); + me->free = latest.numBytesFree; + me->pagesize = 1; + me->flag = F_MEM_TOTAL | F_MEM_FREE; + return 1; +} +#endif + + +#if defined(__linux__) /* ifdef SYSINFO */ +/* sysinfo does not include cached memory which is a problem. */ +static int +get_extended_mem_sysinfo(memory_ext *me) { + struct sysinfo info; + me->flag = 0; + if (sysinfo(&info) < 0) return -1; + me->pagesize = 1; + me->total = info.totalram; + me->free = info.freeram; + me->buffered = info.bufferram; + me->shared = info.sharedram; + me->total_swap = info.totalswap; + me->free_swap = info.freeswap; + + me->flag = F_MEM_TOTAL | F_MEM_FREE | F_MEM_SHARED | F_MEM_BUFFERS | F_SWAP_TOTAL | F_SWAP_FREE; + + return 1; +} +#endif + + +#if defined(_SC_AVPHYS_PAGES) +static int +get_extended_mem_sysconf(memory_ext *me) { + me->total = sysconf(_SC_PHYS_PAGES); + me->free = sysconf(_SC_AVPHYS_PAGES); + me->pagesize = sysconf(_SC_PAGESIZE); + + me->flag = F_MEM_TOTAL | F_MEM_FREE; + + return 1; +} +#endif + +#if defined(BSD4_4) +static int +get_extended_mem_bsd4(memory_ext *me) { + struct vmtotal vt; + long pgsz; + + if (!get_vmtotal(&vt)) return 0; + if ((pgsz = sysconf(_SC_PAGESIZE)) == -1) return 0; + + me->total = (vt.t_free + vt.t_rm); + me->free = vt.t_free; + me->pagesize = pgsz; + + me->flag = F_MEM_TOTAL | F_MEM_FREE; + + return 1; +} +#endif + +#if defined(sgi) || defined(__sgi) || defined(__sgi__) +static int +get_extended_mem_sgi(memory_ext *me) { + struct rminfo rmi; + if (sysmp(MP_SAGET, MPSA_RMINFO, &rmi, sizeof(rmi)) < 0) return -1; + + me->total = (unsigned long)(rmi.physmem); + me->free = (unsigned long)(rmi.freemem); + me->pagesize = (unsigned long)getpagesize(); + me->flag = F_MEM_TOTAL | F_MEM_FREE; + + return 1; +} +#endif + +static void +get_extended_mem(memory_ext *me) { +/* vxworks */ +#if defined(VXWORKS) + if (get_extended_mem_vxworks(me)) return; + +/* linux */ +#elif defined(__linux__) + if (get_mem_procfs(me)) return; + if (get_extended_mem_sysinfo(me)) return; + +/* bsd */ +#elif defined(BSD4_4) + if (get_extended_mem_bsd4(me)) return; + +/* sgi */ +#elif defined(sgi) || defined(__sgi) || defined(__sgi__) + if (get_extended_mem_sgi(me)) return; +#endif + +/* Does this exist on others than Solaris2? */ +#if defined(_SC_AVPHYS_PAGES) + if (get_extended_mem_sysconf(me)) return; + +/* We fake the rest */ +/* SunOS4 (for example) */ +#else + me->free = (1<<27); /* Fake! 128 MB used */ + me->total = (1<<28); /* Fake! 256 MB total */ + me->pagesize = 1; + me->flag = F_MEM_TOTAL | F_MEM_FREE; +#endif +} + + +static void +get_basic_mem(unsigned long *tot, unsigned long *used, unsigned long *pagesize){ +#if defined(VXWORKS) + load_statistics(); + *tot = (latest.numBytesFree + latest.numBytesAlloc); + *used = latest.numBytesAlloc; + *pagesize = 1; +#elif defined(_SC_AVPHYS_PAGES) /* Does this exist on others than Solaris2? */ + unsigned long avPhys, phys, pgSz; + + phys = sysconf(_SC_PHYS_PAGES); + avPhys = sysconf(_SC_AVPHYS_PAGES); + *used = (phys - avPhys); + *tot = phys; + *pagesize = sysconf(_SC_PAGESIZE); +#elif defined(__linux__) && !defined(_SC_AVPHYS_PAGES) + memory_ext me; + if (get_mem_procfs(&me) < 0) { + print_error("ProcFS read error."); + exit(1); + } + *tot = me.total; + *pagesize = me.pagesize; + *used = me.total - me.free; +#elif defined(BSD4_4) + struct vmtotal vt; + long pgsz; + + if (!get_vmtotal(&vt)) goto fail; + if ((pgsz = sysconf(_SC_PAGESIZE)) == -1) goto fail; + *tot = (vt.t_free + vt.t_rm); + *used = vt.t_rm; + *pagesize = pgsz; + return; +fail: + print_error("%s", strerror(errno)); + exit(1); +#elif defined(sgi) || defined(__sgi) || defined(__sgi__) + struct rminfo rmi; + if (sysmp(MP_SAGET, MPSA_RMINFO, &rmi, sizeof(rmi)) != -1) { + *tot = (unsigned long)(rmi.physmem); + *used = (unsigned long)(rmi.physmem - rmi.freemem); + *pagesize = (unsigned long)getpagesize(); + } else { + print_error("%s", strerror(errno)); + exit(1); + } +#else /* SunOS4 */ + *used = (1<<27); /* Fake! 128 MB used */ + *tot = (1<<28); /* Fake! 256 MB total */ + *pagesize = 1; +#endif +} + +static void +simple_show_mem(void){ + unsigned long tot, used, pagesize; + get_basic_mem(&tot, &used, &pagesize); + send(used, pagesize); + send(tot, pagesize); +} + +static void +extended_show_mem(void){ + memory_ext me; + unsigned long ps; + + get_extended_mem(&me); + ps = me.pagesize; + + if (me.flag & F_MEM_TOTAL) { send_tag(MEM_TOTAL); send(me.total, ps); } + if (me.flag & F_MEM_FREE) { send_tag(MEM_FREE); send(me.free, ps); } + + /* extensions */ + if (me.flag & F_MEM_BUFFERS){ send_tag(MEM_BUFFERS); send(me.buffered, ps); } + if (me.flag & F_MEM_CACHED) { send_tag(MEM_CACHED); send(me.cached, ps); } + if (me.flag & F_MEM_SHARED) { send_tag(MEM_SHARED); send(me.shared, ps); } + + /* swap */ + if (me.flag & F_SWAP_TOTAL) { send_tag(SWAP_TOTAL); send(me.total_swap, ps); } + if (me.flag & F_SWAP_FREE) { send_tag(SWAP_FREE); send(me.free_swap, ps); } + +#ifdef VXWORKS + send_tag(SM_SYSTEM_TOTAL); + send(latest_system_total, 1); + send_tag(SM_LARGEST_FREE); + send(latest.maxBlockSizeFree, 1); + send_tag(SM_NUMBER_OF_FREE); + send(latest.numBlocksFree, 1); +#else + /* total is system total*/ + if (me.flag & F_MEM_TOTAL) { send_tag(MEM_SYSTEM_TOTAL); send(me.total, ps); } +#endif + send_tag(SHOW_SYSTEM_MEM_END); +} + +static void +message_loop(int erlin_fd) +{ + char cmdLen, cmd; + int res; + + while (1){ + /* + * Wait for command from Erlang + */ + if ((res = read(erlin_fd, &cmdLen, 1)) < 0) { + print_error("Error reading from Erlang."); + return; + } + + if (res == 1) { /* Exactly one byte read ? */ + if (cmdLen == 1){ /* Should be! */ + switch (read(erlin_fd, &cmd, 1)){ + case 1: + switch (cmd){ + case SHOW_MEM: + simple_show_mem(); + break; + case SHOW_SYSTEM_MEM: + extended_show_mem(); + break; + default: /* ignore all other messages */ + break; + } + break; + + case 0: + print_error("Erlang has closed."); + return; + + default: + print_error("Error reading from Erlang."); + return; + } /* switch() */ + } else { /* cmdLen != 1 */ + print_error("Invalid command length (%d) received.", cmdLen); + return; + } + } else { /* Erlang end closed */ + print_error("Erlang has closed."); + return; + } + } +} + +/* + * main + */ +int +MAIN(int argc, char **argv) +{ + program_name = argv[0]; + message_loop(ERLIN_FD); + return 0; +} + + +/* + * print_error + * + */ +static void +print_error(const char *format,...) +{ + va_list args; + + va_start(args, format); + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, " \n"); +} + + + + + diff --git a/lib/os_mon/c_src/memsup.h b/lib/os_mon/c_src/memsup.h new file mode 100644 index 0000000000..926df0a847 --- /dev/null +++ b/lib/os_mon/c_src/memsup.h @@ -0,0 +1,46 @@ +/* + * %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% + */ +/* + * This header defines the protocol between the erlang + * memsup module and the C module. + */ +#ifndef _SYSMEM_H +#define _SYSMEM_H + +/* Simple memory statistics */ +/*IG*/ #define SHOW_MEM 1 + +/* Extended memory statistics */ +/*IG*/ #define SHOW_SYSTEM_MEM 2 + +/* Tags for the extended statistics */ +/*IG*/ #define SHOW_SYSTEM_MEM_END 0 +/*IG*/ #define MEM_SYSTEM_TOTAL 1 +/*IG*/ #define MEM_TOTAL 2 +/*IG*/ #define MEM_FREE 3 +/*IG*/ #define MEM_LARGEST_FREE 4 +/*IG*/ #define MEM_NUMBER_OF_FREE 5 +/*Extension*/ +/*IG*/ #define MEM_BUFFERS 6 +/*IG*/ #define MEM_CACHED 7 +/*IG*/ #define MEM_SHARED 8 +/*IG*/ #define SWAP_TOTAL 9 +/*IG*/ #define SWAP_FREE 10 + +#endif diff --git a/lib/os_mon/c_src/mod_syslog.c b/lib/os_mon/c_src/mod_syslog.c new file mode 100644 index 0000000000..87fbfbac22 --- /dev/null +++ b/lib/os_mon/c_src/mod_syslog.c @@ -0,0 +1,137 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-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 +#include +#include +#include +#include +#include + + /* arguments */ +#define MODE 1 +#define OWNPATH 2 +#define SYSLOGCONF 3 +#define NARG 3 + + /* status codes */ +#define OK 0 +#define ARG_ERROR -1 +#define FILE_ERROR -2 +#define KILL_ERROR -3 +#define PIPE_NOT_FOUND 1 + + /* hardwired */ +#define SYSLOG_PID "/etc/syslog.pid" +#define PIPENAME "syslog.otp" +#define SYSLOGCONF_ORIG "syslog.conf.ORIG" +#define SYSLOGCONF_OTP "syslog.conf.OTP" +#define BUFFER_SIZE 1001 +#define MAXNAME_SIZE 1001 +#define FALSE 0 +#define TRUE 1 +#define WAIT 1 + +main(int argc, char *argv[]) +/* usage: mod_syslog mode ownpath syslogconf */ +{ + int syslogd_pid, n_lines_copied=0; + int otp_sw, find_sw=FALSE; + char buf[BUFFER_SIZE], pipename[MAXNAME_SIZE], srcname[MAXNAME_SIZE]; + FILE *srcfile, *destfile, *pidfile; + + void make_exit(int); + + /* enough arguments? */ + if(argc < NARG+1) + make_exit(ARG_ERROR); + + /* enable OTP or not */ + if(strcmp(argv[MODE], "otp") == 0) + otp_sw = TRUE; + else if(strcmp(argv[MODE], "nootp") == 0) + otp_sw = FALSE; + else + make_exit(ARG_ERROR); + + /* make pipename */ + strcpy(pipename, argv[OWNPATH]); + strcat(pipename, "/"); + strcat(pipename, PIPENAME); + + /* remove old pipe and make a new one */ + if(otp_sw){ + /* remove */ + unlink(pipename); + + /* make a new */ + if(mknod(pipename, S_IFIFO | S_IRUSR | S_IWUSR | S_IRGRP | + S_IWGRP | S_IROTH | S_IWOTH, (dev_t)0) != 0) + make_exit(FILE_ERROR); + } + + /* make source filename */ + strcpy(srcname, argv[OWNPATH]); + strcat(srcname, "/"); + strcat(srcname, otp_sw?SYSLOGCONF_OTP:SYSLOGCONF_ORIG); + + /* open source and destination, exit if error */ + if((srcfile = fopen(srcname, "r")) == NULL || + (destfile = fopen(argv[SYSLOGCONF], "w")) == NULL) + make_exit(FILE_ERROR); + + /* copy source and destination, exit if error */ + while(fgets(buf, BUFFER_SIZE-1, srcfile) != NULL){ + /* find_sw |= strstr(buf, PIPENAME) != NULL; */ + n_lines_copied++; + if(fputs(buf, destfile) == EOF) + make_exit(FILE_ERROR); + } + if(ferror(srcfile) || n_lines_copied == 0) + make_exit(FILE_ERROR); + + /* open pidfile, exit if error */ + if((pidfile = fopen(SYSLOG_PID, "r")) == NULL) + make_exit(FILE_ERROR); + + /* read pid for syslogd, exit if error */ + if(fscanf(pidfile, "%d", &syslogd_pid) == 0) + make_exit(FILE_ERROR); + + /* send HUP to syslogd, exit if error */ + if(syslogd_pid < 0 || kill((pid_t)syslogd_pid, SIGHUP) != 0) + make_exit(KILL_ERROR); + + /* remove pipe */ + if(!otp_sw){ + sleep((unsigned)WAIT); + unlink(pipename); + } + + /* ending successful or with warning */ + /* if(!find_sw && otp_sw) + make_exit(PIPE_NOT_FOUND); + else */ + make_exit(OK); +} + +void make_exit(int exit_code) +{ + printf("%d", exit_code); + exit(0); +} 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 +#include +#include +#include +#include +#include +#include +#include +#include "winbase.h" + +#define MEM_INFO 'm' +#define DISK_INFO 'd' +#define OK "o" + +#define ERLIN_FD 0 +#define ERLOUT_FD 1 + + +typedef BOOL (WINAPI *tfpGetDiskFreeSpaceEx)(LPCTSTR, PULARGE_INTEGER,PULARGE_INTEGER,PULARGE_INTEGER); + +static tfpGetDiskFreeSpaceEx fpGetDiskFreeSpaceEx; + +static void +return_answer(char* value) +{ + int left, bytes, res; + + bytes = strlen(value); /* Skip trailing zero */ + + res = write(1,(char*) &bytes,1); + if (res != 1) { + fprintf(stderr,"win32sysinfo:Error writing to pipe"); + exit(1); + } + + left = bytes; + + while (left > 0) + { + res = write(1, value+bytes-left, left); + if (res <= 0) + { + fprintf(stderr,"win32sysinfo:Error writing to pipe"); + exit(1); + } + left -= res; + } +} + +void output_drive_info(char* drive){ + ULARGE_INTEGER availbytes,totbytesfree,totbytes; + OSVERSIONINFO osinfo; + char answer[512]; + osinfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO); + GetVersionEx(&osinfo); + switch (GetDriveType(drive)) { + case DRIVE_UNKNOWN: + sprintf(answer,"%s DRIVE_UNKNOWN 0 0 0\n",drive); + return_answer(answer); + break; + case DRIVE_NO_ROOT_DIR: + sprintf(answer,"%s DRIVE_NO_ROOT_DIR 0 0 0\n",drive); + return_answer(answer); + break; + case DRIVE_REMOVABLE: + sprintf(answer,"%s DRIVE_REMOVABLE 0 0 0\n",drive); + return_answer(answer); + break; + case DRIVE_FIXED: + /* if ((osinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && + (LOWORD(osinfo.dwBuildNumber) <= 1000)) { + sprintf(answer,"%s API_NOT_SUPPORTED 0 0 0\n",drive); + return_answer(answer); + } + else + */ + if (fpGetDiskFreeSpaceEx == NULL){ + sprintf(answer,"%s API_NOT_SUPPORTED 0 0 0\n",drive); + return_answer(answer); + } + else + if (fpGetDiskFreeSpaceEx(drive,&availbytes,&totbytes,&totbytesfree)){ + sprintf(answer,"%s DRIVE_FIXED %I64u %I64u %I64u\n",drive,availbytes,totbytes,totbytesfree); + return_answer(answer); + } + else { + sprintf(answer,"%s API_ERROR 0 0 0\n",drive); + return_answer(answer); + } + break; + case DRIVE_REMOTE: + sprintf(answer,"%s DRIVE_REMOTE 0 0 0\n",drive); + return_answer(answer); + break; + case DRIVE_CDROM: + sprintf(answer,"%s DRIVE_CDROM 0 0 0\n",drive); + return_answer(answer); + break; + case DRIVE_RAMDISK: + sprintf(answer,"%s DRIVE_RAMDISK 0 0 0\n",drive); + return_answer(answer); + break; + default: + sprintf(answer,"%s DRIVE_NOT_EXIST 0 0 0\n",drive); + return_answer(answer); + } /* switch */ +} + +int load_if_possible() { + HINSTANCE lh; + if((lh = LoadLibrary("KERNEL32")) ==NULL) + return 0; /* error */ + if ((fpGetDiskFreeSpaceEx = + (tfpGetDiskFreeSpaceEx) GetProcAddress(lh,"GetDiskFreeSpaceExA")) ==NULL) + return GetLastError(); /* error */ + return 1; +} + +void get_disk_info_all(){ + DWORD dwNumBytesForDriveStrings; + char DriveStrings[255]; + char* dp = DriveStrings; + + dwNumBytesForDriveStrings = GetLogicalDriveStrings(254,dp); + if (dwNumBytesForDriveStrings != 0) { + /* GetLogicalDriveStringsIs supported on this platform */ + while (*dp != 0) { + output_drive_info(dp); + dp = strchr(dp,0) +1; + } + } + else { + /* GetLogicalDriveStrings is not supported (some old W95) */ + DWORD dwDriveMask = GetLogicalDrives(); + int nDriveNum; + char drivename[]="A:\\"; + /*printf("DriveName95 DriveType BytesAvail BytesTotal BytesTotalFree\n");*/ + for (nDriveNum = 0; dwDriveMask != 0;nDriveNum++) { + if (dwDriveMask & 1) { + drivename[0]='A'+ nDriveNum; + output_drive_info(drivename); + } + dwDriveMask = dwDriveMask >> 1; + } + } +} + +void get_avail_mem_ext() { + char answer[512]; + MEMORYSTATUSEX ms; + ms.dwLength=sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx(&ms); + sprintf(answer,"%d %I64d %I64d %I64d %I64d %I64d %I64d\n", + ms.dwMemoryLoad, + ms.ullTotalPhys, + ms.ullAvailPhys, + ms.ullTotalPageFile, + ms.ullAvailPageFile, + ms.ullTotalVirtual, + ms.ullAvailVirtual + ); + return_answer(answer); + /* + DWORD dwLength; + DWORD dwMemoryLoad; + DWORDLONG ullTotalPhys; + DWORDLONG ullAvailPhys; + DWORDLONG ullTotalPageFile; + DWORDLONG ullAvailPageFile; + DWORDLONG ullTotalVirtual; + DWORDLONG ullAvailVirtual; + */ +} + +static void +message_loop() +{ + char cmdLen; + char cmd[512]; + int res; + + fprintf(stderr,"in message_loop\n"); + /* Startup ACK. */ + return_answer(OK); + while (1) + { + /* + * Wait for command from Erlang + */ + if ((res = read(0, &cmdLen, 1)) < 0) { + fprintf(stderr,"win32sysinfo:Error reading from Erlang."); + return; + } + + if (res != 1){ /* Exactly one byte read ? */ + fprintf(stderr,"win32sysinfo:Erlang has closed."); + return; + } + if ((res = read(0, &cmd, cmdLen)) == cmdLen){ + if (cmdLen == 1) { + switch (cmd[0]) { + case MEM_INFO: + get_avail_mem_ext(); + return_answer(OK); + break; + case DISK_INFO: + get_disk_info_all(); + return_answer(OK); + break; + default: /* ignore all other messages */ + break; + } /* switch */ + } + else + if ((res > 0) && (cmd[0]==DISK_INFO)) { + cmd[cmdLen] = 0; + output_drive_info(&cmd[1]); + return_answer("OK"); + return; + } + else + return_answer("xEND"); + } + else if (res == 0) { + fprintf(stderr,"win32sysinfo:Erlang has closed."); + return; + } + else { + fprintf(stderr,"win32sysinfo:Error reading from Erlang."); + return; + } + } +} + +int main(int argc, char ** argv){ + + _setmode(0, _O_BINARY); + _setmode(1, _O_BINARY); + load_if_possible(); + message_loop(); + return 0; +} + + + + + + + -- cgit v1.2.3