aboutsummaryrefslogtreecommitdiffstats
path: root/lib/os_mon/c_src
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/os_mon/c_src
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/os_mon/c_src')
-rw-r--r--lib/os_mon/c_src/Makefile5
-rw-r--r--lib/os_mon/c_src/Makefile.in144
-rw-r--r--lib/os_mon/c_src/cpu_sup.c455
-rw-r--r--lib/os_mon/c_src/ferrule.c160
-rw-r--r--lib/os_mon/c_src/memsup.c655
-rw-r--r--lib/os_mon/c_src/memsup.h46
-rw-r--r--lib/os_mon/c_src/mod_syslog.c137
-rw-r--r--lib/os_mon/c_src/nteventlog/elog_format.c173
-rw-r--r--lib/os_mon/c_src/nteventlog/elog_format.h40
-rw-r--r--lib/os_mon/c_src/nteventlog/elog_global.h57
-rw-r--r--lib/os_mon/c_src/nteventlog/elog_main.c504
-rw-r--r--lib/os_mon/c_src/nteventlog/elog_pipe_stdin.c151
-rw-r--r--lib/os_mon/c_src/nteventlog/elog_pipe_stdin.h90
-rw-r--r--lib/os_mon/c_src/nteventlog/elog_registry.c295
-rw-r--r--lib/os_mon/c_src/nteventlog/elog_registry.h67
-rw-r--r--lib/os_mon/c_src/nteventlog/elog_util.c73
-rw-r--r--lib/os_mon/c_src/nteventlog/elog_util.h79
-rw-r--r--lib/os_mon/c_src/win32sysinfo.c318
18 files changed, 3449 insertions, 0 deletions
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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#if defined(__sun__)
+#include <kstat.h>
+#endif
+
+#include <sys/sysinfo.h>
+#include <errno.h>
+
+#if defined(__linux__)
+#include <string.h> /* 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 <stdio.h>
+#include <string.h>
+#include <stropts.h>
+#include <poll.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/uio.h>
+
+ /* 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 <sys/types.h>
+#include <sys/sysmp.h>
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#ifndef VXWORKS
+#include <unistd.h>
+#endif
+
+#if (defined(__unix__) || defined(unix)) && !defined(USG)
+#include <sys/param.h>
+#endif
+
+#include <stdarg.h>
+
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#ifdef VXWORKS
+#include <vxWorks.h>
+#include <ioLib.h>
+#include <memLib.h>
+#endif
+
+#ifdef BSD4_4
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#if !defined (__OpenBSD__) && !defined (__NetBSD__)
+#include <vm/vm_param.h>
+#endif
+#if defined (__FreeBSD__) || defined(__DragonFly__)
+#include <sys/vmmeter.h>
+#endif
+#endif
+
+#if defined (__linux__)
+#include <sys/sysinfo.h>
+#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 <fcntl.h>
+#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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+ /* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <process.h>
+
+#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<rklen; ++i){
+ elog[i] = OpenEventLog(NULL, rk[i].facility_name);
+
+ if(elog[i] == NULL){
+ fprintf(stderr,
+ "%s: Could not open \"%s\" eventlog.\n",
+ argv[0], rk[i].facility_name);
+ return 1;
+ }
+ if((events[i] = CreateEvent(NULL,FALSE,FALSE,NULL))
+ == NULL){
+ fprintf(stderr,"%s: Could not create event object.\n", argv[0]);
+ return 1;
+ }
+ if(!NotifyChangeEventLog(elog[i],events[i])){
+ fprintf(stderr,"%s: Could not get eventlog notification.\n", argv[0]);
+ return 1;
+ }
+ if(read_from(&(rk[i].latest_record),
+ &(rk[i].latest_time),
+ elog[i],
+ rk[i].facility_name) > 0)
+ set_regkeys(ident, rk + i, 1);
+ }
+ eventlen = rklen;
+ events[eventlen] = get_stdin_event();
+ ++eventlen;
+
+ for(;;){
+#ifdef HARDDEBUG
+ fprintf(stderr,"Entering Wait...\n");
+#endif
+ reopen_event_log = 0;
+ ret = WaitForMultipleObjects(eventlen,
+ events,
+ FALSE,
+ (retry) ?
+ RETRY_TIMEOUT :
+ INFINITE);
+#ifdef HARDDEBUG
+ fprintf(stderr,"Wait returned!\n");
+#endif
+ if(ret == WAIT_TIMEOUT){
+ if(!retry){
+ fprintf(stderr,"%s: Timeout when no such possible!\n",
+ argv[0]);
+ return 1;
+ }
+ retry = 0;
+ } else {
+ if(((int) (ret - WAIT_OBJECT_0)) >= rklen && eof_on_stdin())
+ goto done;
+ retry = 1;
+ }
+ for(i=0;i<rklen;++i){
+ if(read_from(&(rk[i].latest_record),
+ &(rk[i].latest_time),
+ elog[i],
+ rk[i].facility_name) > 0)
+ set_regkeys(ident, rk + i, 1);
+ }
+ if(reopen_event_log)
+ break;
+ }
+ for(i=0; i < rklen; ++i){
+ CloseEventLog(elog[i]);
+ CloseHandle(events[i]);
+ }
+ }
+done:
+#ifdef DEBUG
+ fprintf(stderr,"%s: EOF\n", argv[0]);
+#endif
+ for(i=0; i < rklen; ++i)
+ CloseEventLog(elog[i]);
+ return 0;
+}
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<num_keys; ++i){
+ sprintf(knbuff,"%s\\%s\\%s\\%s\\%s",
+ APP_ROOT_KEY,
+ APP_SUB_KEY,
+ APP_VERSION,
+ identifier,
+ keys[i].facility_name);
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ knbuff,
+ 0,
+ KEY_SET_VALUE,
+ &key) != ERROR_SUCCESS){
+ return -1;
+ }
+ if(RegSetValueEx(key,
+ LATEST_RECORD_NAME,
+ 0,
+ REG_DWORD,
+ (BYTE *) &(keys[i].latest_record),
+ sizeof(DWORD)) != ERROR_SUCCESS){
+ RegCloseKey(key);
+ return -1;
+ }
+ if(RegSetValueEx(key,
+ LATEST_TIME_NAME,
+ 0,
+ REG_DWORD,
+ (BYTE *) &(keys[i].latest_time),
+ sizeof(DWORD)) != ERROR_SUCCESS){
+ RegCloseKey(key);
+ return -1;
+ }
+ RegCloseKey(key);
+ }
+ return 0;
+}
+
+
+int get_regkeys(char *identifier, RegKeys *keys, int *num_keys /* in out */){
+ HKEY key,key2;
+ int i;
+ char knbuff[SMALLBUFSIZ];
+ DWORD knlen;
+ char cnbuff[SMALLBUFSIZ];
+ DWORD cnlen;
+ FILETIME ft;
+ DWORD type, data, datasiz;
+
+ sprintf(knbuff,"%s\\%s\\%s\\%s",
+ APP_ROOT_KEY,
+ APP_SUB_KEY,
+ APP_VERSION,
+ identifier);
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ knbuff,
+ 0,
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
+ &key) != ERROR_SUCCESS){
+ if(create_regkeys(identifier)!= 0){
+#ifdef HARDDEBUG
+ fprintf(stderr,"Failed creating regkeys\n");
+#endif
+ return -1;
+ } else {
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ knbuff,
+ 0,
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
+ &key) != ERROR_SUCCESS){
+#ifdef HARDDEBUG
+ fprintf(stderr,"Failed opening regkeys\n");
+#endif
+ return -1;
+ }
+ }
+ }
+ for(i = 0, knlen = SMALLBUFSIZ, cnlen = SMALLBUFSIZ;
+ i < *num_keys &&
+ ERROR_SUCCESS == RegEnumKeyEx(key,
+ i,
+ knbuff,
+ &knlen,
+ NULL,
+ cnbuff,
+ &cnlen,
+ &ft);
+ ++i, knlen = SMALLBUFSIZ, cnlen = SMALLBUFSIZ){
+ if(RegOpenKeyEx(key,
+ knbuff,
+ 0,
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
+ &key2) != ERROR_SUCCESS)
+ continue;
+ strncpy(keys[i].facility_name, knbuff,
+ MAX_FACILITY_NAME);
+ keys[i].facility_name[MAX_FACILITY_NAME - 1] = '\0';
+ datasiz = sizeof(DWORD);
+ if(RegQueryValueEx(key2,
+ LATEST_RECORD_NAME,
+ NULL,
+ &type,
+ (BYTE *) &data,
+ &datasiz) != ERROR_SUCCESS)
+ keys[i].latest_record = 0;
+ else
+ keys[i].latest_record = data;
+ if(RegQueryValueEx(key2,
+ LATEST_TIME_NAME,
+ NULL,
+ &type,
+ (BYTE *) &data,
+ &datasiz) != ERROR_SUCCESS)
+ keys[i].latest_time = 0;
+ else
+ keys[i].latest_time = data;
+ RegCloseKey(key2);
+ }
+ *num_keys = i;
+ if(!*num_keys){
+#ifdef HARDDEBUG
+ fprintf(stderr,"get_regkeys got none!\n");
+#endif
+ return -1;
+ }
+ return 0;
+}
diff --git a/lib/os_mon/c_src/nteventlog/elog_registry.h b/lib/os_mon/c_src/nteventlog/elog_registry.h
new file mode 100644
index 0000000000..32876ae8bf
--- /dev/null
+++ b/lib/os_mon/c_src/nteventlog/elog_registry.h
@@ -0,0 +1,67 @@
+/*
+ * %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_REGISTRY_H
+#define _ELOG_REGISTRY_H
+/*
+ * Module: elog_registry
+ * Purpouse: Handle the registry reading and writing.
+ */
+
+#include "elog_global.h"
+
+/*
+ * A structure for the registry keys.
+ */
+typedef struct _reg_keys {
+ char facility_name[MAX_FACILITY_NAME];
+ DWORD latest_record;
+ DWORD latest_time;
+} RegKeys;
+
+MessageFiles get_messagefiles(const char *category, const char *facility,
+ char *eventbuff, int eventbufflen,
+ char *parambuff, int parambufflen);
+/*
+ * Return the EventMessageFile and then ParameterMessageFile for
+ * a facility under a category.
+ * The buffers should be at least MAX_PATH long to make
+ * sure the filenames can be stored.
+ * If facility is not found, both fields in the record are NULL,
+ * if the ParameterMessageFile is not found, the param filed of the record
+ * is NULL.
+ */
+
+int create_regkeys(char *identifier);
+/*
+ * Creates registry entries for this log identifier
+ */
+int set_regkeys(char *identifier, RegKeys *keys, int num_keys);
+/*
+ * Updates the registry keys for the identifier. Multiple
+ * categories can be specified in the keys array.
+ */
+int get_regkeys(char *identifier, RegKeys *keys, int *num_keys /* in out */);
+/*
+ * Reads the keys from the registry database for this
+ * identifier, creating them if needed. The values
+ * for the different categories are stored in the
+ * keys array as long as there is place.
+ */
+
+#endif /* _ELOG_REGISTRY_H */
diff --git a/lib/os_mon/c_src/nteventlog/elog_util.c b/lib/os_mon/c_src/nteventlog/elog_util.c
new file mode 100644
index 0000000000..ac14f2f3e4
--- /dev/null
+++ b/lib/os_mon/c_src/nteventlog/elog_util.c
@@ -0,0 +1,73 @@
+/*
+ * %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"
+
+char *category_tab[] = {
+ SYSTEM,
+ APPLICATION,
+ SECURITY,
+ NULL
+};
+
+SeverityEntry severity_tab[] = {
+ {EVENTLOG_ERROR_TYPE , "Error"},
+ {EVENTLOG_WARNING_TYPE, "Warning"},
+ {EVENTLOG_INFORMATION_TYPE, "Informational"},
+ {EVENTLOG_AUDIT_SUCCESS, "Audit_Success"},
+ {EVENTLOG_AUDIT_FAILURE, "Audit_Faulure"},
+ {0, "Severity_Unknown"}
+};
+
+int severity_tab_len =
+sizeof(severity_tab)/sizeof(SeverityEntry);
+
+char *lookup_severity(WORD severity){
+ int i;
+ for(i=0;i<severity_tab_len-1;++i)
+ if(severity_tab[i].type == severity)
+ return severity_tab[i].desc;
+ return severity_tab[i].desc;
+}
+
+
+void output_error(DWORD error, char *extra){
+ char *msgbuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ error,
+ DEFAULT_LANGID,
+ (LPTSTR) &msgbuf,
+ 0,
+ NULL );
+ fprintf(stderr,"%s: %s", extra, msgbuf);
+ LocalFree(msgbuf);
+}
+
+
+#undef malloc
+void *my_malloc(size_t siz){
+ void *x;
+ if((x = malloc(siz)) == NULL){
+ fputs("Out of memory.", stderr);
+ exit(1);
+ }
+ return x;
+}
diff --git a/lib/os_mon/c_src/nteventlog/elog_util.h b/lib/os_mon/c_src/nteventlog/elog_util.h
new file mode 100644
index 0000000000..66f1a2519e
--- /dev/null
+++ b/lib/os_mon/c_src/nteventlog/elog_util.h
@@ -0,0 +1,79 @@
+/*
+ * %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_UTIL_H
+#define _ELOG_UTIL_H
+/*
+ * Module: elog_util
+ * Purpouse: Utilities for other elog modules.
+ */
+
+#include "elog_global.h"
+
+/*
+ * A table of eventlog categories in an NT system. NOTE that the NUM_CATEGORIES
+ * constant has to be updated if this table is!
+ */
+extern char *category_tab[];
+
+#define NUM_CATEGORIES 3
+
+/*
+ * Table for translation of severity constants o strings
+ */
+typedef struct _severity_tab {
+ WORD type;
+ char *desc;
+} SeverityEntry;
+
+extern SeverityEntry severity_tab[];
+extern int severity_tab_len;
+
+char *lookup_severity(WORD severity);
+/*
+ * Lookup in the above table.
+ */
+
+/*
+ * Safe malloc.
+ */
+void *my_malloc(size_t siz);
+#define malloc(X) my_malloc(X)
+
+/*
+ * Error reporting
+ */
+void output_error(DWORD error, char *extra);
+/*
+ * Formats a GetLastError() errorcode to text,
+ * prepended with 'extra':. Writes to stderr.
+ */
+
+/*
+ * I don't like mistyped strings...
+ */
+#define SYSTEM "System"
+#define APPLICATION "Application"
+#define SECURITY "Security"
+/*
+ * The mask to get the printable message id value
+ */
+#define EVENT_ID_MASK 0x0000FFFF
+
+#endif /* _ELOG_UTIL_H */
+
diff --git a/lib/os_mon/c_src/win32sysinfo.c b/lib/os_mon/c_src/win32sysinfo.c
new file mode 100644
index 0000000000..2a155aae87
--- /dev/null
+++ b/lib/os_mon/c_src/win32sysinfo.c
@@ -0,0 +1,318 @@
+/*
+ * %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%
+ */
+
+/**
+ * win32sysinfo.c
+ *
+ * File: win32sysinfo.c
+ * Purpose: Portprogram for supervision of disk and memory usage.
+ *
+ * Synopsis: win32sysinfo
+ *
+ * PURPOSE OF THIS PROGRAM
+ *
+ * This program supervises the reports the memory status or disk status
+ * on 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
+ *
+ * COMMUNICATION
+ *
+ * WIN32
+ *
+ * get_disk_info 'd' (request info about all drives)
+ * The result is returned as one packet per logical drive with the
+ * following format:
+ * Drive Type AvailableBytes TotalBytes TotalBytesFree
+ * END
+ *
+ * Example:
+ * A:\ DRIVE_REMOVABLE 0 0 0
+ * C:\ DRIVE_FIXED 10000000 20000000 10000000
+ * END
+
+ * get_disk_info 'd'Driveroot (where Driveroot is a string like this "A:\\"
+ * (request info of specific drive)
+ * The result is returned with the same format as above exept that
+ * Type will be DRIVE_NOT_EXIST if the drive does not exist.
+ *
+ * get_mem_info 'm' (request info about memory)
+ *
+ * The result is returned as one packet with the following format
+ *
+ *
+ *
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <windows.h>
+#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;
+}
+
+
+
+
+
+
+