aboutsummaryrefslogtreecommitdiffstats
path: root/lib/os_mon
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
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/os_mon')
-rw-r--r--lib/os_mon/AUTHORS12
-rw-r--r--lib/os_mon/Makefile40
-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
-rw-r--r--lib/os_mon/doc/html/.gitignore0
-rw-r--r--lib/os_mon/doc/man3/.gitignore0
-rw-r--r--lib/os_mon/doc/man6/.gitignore0
-rw-r--r--lib/os_mon/doc/pdf/.gitignore0
-rw-r--r--lib/os_mon/doc/src/Makefile124
-rw-r--r--lib/os_mon/doc/src/book.xml45
-rw-r--r--lib/os_mon/doc/src/cpu_sup.xml281
-rw-r--r--lib/os_mon/doc/src/disksup.xml161
-rw-r--r--lib/os_mon/doc/src/fascicules.xml15
-rw-r--r--lib/os_mon/doc/src/make.dep21
-rw-r--r--lib/os_mon/doc/src/memsup.xml328
-rw-r--r--lib/os_mon/doc/src/note.gifbin0 -> 1539 bytes
-rw-r--r--lib/os_mon/doc/src/notes.xml537
-rw-r--r--lib/os_mon/doc/src/nteventlog.xml104
-rw-r--r--lib/os_mon/doc/src/os_mon_app.xml126
-rw-r--r--lib/os_mon/doc/src/os_mon_mib.xml70
-rw-r--r--lib/os_mon/doc/src/os_sup.xml241
-rw-r--r--lib/os_mon/doc/src/part_notes.xml36
-rw-r--r--lib/os_mon/doc/src/ref_man.xml42
-rw-r--r--lib/os_mon/doc/src/user_guide.gifbin0 -> 1581 bytes
-rw-r--r--lib/os_mon/doc/src/warning.gifbin0 -> 1498 bytes
-rw-r--r--lib/os_mon/ebin/.gitignore0
-rw-r--r--lib/os_mon/include/memsup.hrl43
-rw-r--r--lib/os_mon/info3
-rw-r--r--lib/os_mon/mibs/Makefile97
-rw-r--r--lib/os_mon/mibs/OTP-OS-MON-MIB.funcs5
-rw-r--r--lib/os_mon/mibs/OTP-OS-MON-MIB.mib422
-rw-r--r--lib/os_mon/mibs/v1/.gitignore0
-rw-r--r--lib/os_mon/priv/bin/.gitignore0
-rw-r--r--lib/os_mon/priv/mibs/.gitignore0
-rw-r--r--lib/os_mon/priv/obj/.gitignore0
-rw-r--r--lib/os_mon/src/Makefile110
-rw-r--r--lib/os_mon/src/cpu_sup.erl776
-rw-r--r--lib/os_mon/src/disksup.erl369
-rw-r--r--lib/os_mon/src/memsup.erl1022
-rw-r--r--lib/os_mon/src/nteventlog.erl162
-rw-r--r--lib/os_mon/src/os_mon.app.src32
-rw-r--r--lib/os_mon/src/os_mon.appup.src41
-rw-r--r--lib/os_mon/src/os_mon.erl179
-rw-r--r--lib/os_mon/src/os_mon_mib.erl250
-rw-r--r--lib/os_mon/src/os_mon_sysinfo.erl147
-rw-r--r--lib/os_mon/src/os_sup.erl258
-rw-r--r--lib/os_mon/vsn.mk1
63 files changed, 9549 insertions, 0 deletions
diff --git a/lib/os_mon/AUTHORS b/lib/os_mon/AUTHORS
new file mode 100644
index 0000000000..22e34f8278
--- /dev/null
+++ b/lib/os_mon/AUTHORS
@@ -0,0 +1,12 @@
+Original Authors and Contributors:
+
+Martin Bj�rklund
+Mats Nilsson
+Magnus Fr�berg
+Janne Lindblad
+Kenneth Lundin
+Patrik Nyblom
+Peter H�gfeldt
+Rickard Green
+Ingela Anderton
+Bj�rn-Egil Dahlberg
diff --git a/lib/os_mon/Makefile b/lib/os_mon/Makefile
new file mode 100644
index 0000000000..7eeec1577c
--- /dev/null
+++ b/lib/os_mon/Makefile
@@ -0,0 +1,40 @@
+#
+# %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 $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+#
+# Macros
+#
+ifeq ($(findstring win32,$(TARGET)),win32)
+SUB_DIRECTORIES = src c_src mibs doc/src
+else
+SUB_DIRECTORIES = src c_src mibs doc/src
+endif
+
+include vsn.mk
+VSN = $(OS_MON_VSN)
+
+SPECIAL_TARGETS =
+
+#
+# Default Subdir Targets
+#
+include $(ERL_TOP)/make/otp_subdir.mk
+
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;
+}
+
+
+
+
+
+
+
diff --git a/lib/os_mon/doc/html/.gitignore b/lib/os_mon/doc/html/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/os_mon/doc/html/.gitignore
diff --git a/lib/os_mon/doc/man3/.gitignore b/lib/os_mon/doc/man3/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/os_mon/doc/man3/.gitignore
diff --git a/lib/os_mon/doc/man6/.gitignore b/lib/os_mon/doc/man6/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/os_mon/doc/man6/.gitignore
diff --git a/lib/os_mon/doc/pdf/.gitignore b/lib/os_mon/doc/pdf/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/os_mon/doc/pdf/.gitignore
diff --git a/lib/os_mon/doc/src/Makefile b/lib/os_mon/doc/src/Makefile
new file mode 100644
index 0000000000..c9765749c9
--- /dev/null
+++ b/lib/os_mon/doc/src/Makefile
@@ -0,0 +1,124 @@
+#
+# %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
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(OS_MON_VSN)
+APPLICATION=os_mon
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+XML_APPLICATION_FILES = ref_man.xml
+XML_REF3_FILES = cpu_sup.xml \
+ disksup.xml \
+ memsup.xml \
+ os_mon_mib.xml \
+ os_sup.xml \
+ nteventlog.xml
+
+XML_REF6_FILES = os_mon_app.xml
+
+XML_PART_FILES = part_notes.xml
+XML_CHAPTER_FILES = notes.xml
+
+BOOK_FILES = book.xml
+
+GIF_FILES = \
+ note.gif \
+ warning.gif
+
+XML_FILES = \
+ $(BOOK_FILES) $(XML_CHAPTER_FILES) \
+ $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES) $(XML_APPLICATION_FILES)
+
+# ----------------------------------------------------
+
+HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+
+INFO_FILE = ../../info
+
+MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
+
+MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
+
+HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
+
+TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+XML_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+$(HTMLDIR)/%.gif: %.gif
+ $(INSTALL_DATA) $< $@
+
+docs: pdf html man
+
+$(TOP_PDF_FILE): $(XML_FILES)
+
+pdf: $(TOP_PDF_FILE)
+
+html: gifs $(HTML_REF_MAN_FILE)
+
+man: $(MAN3_FILES) $(MAN6_FILES)
+
+gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+
+debug opt:
+
+clean clean_docs:
+ rm -rf $(HTMLDIR)/*
+ rm -f $(MAN3DIR)/*
+ rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f errs core *~
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_docs_spec: docs
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(HTMLDIR)/* \
+ $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
+ $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man6
+ $(INSTALL_DATA) $(MAN6DIR)/* $(RELEASE_PATH)/man/man6
+
+release_spec:
+
diff --git a/lib/os_mon/doc/src/book.xml b/lib/os_mon/doc/src/book.xml
new file mode 100644
index 0000000000..17645b81fd
--- /dev/null
+++ b/lib/os_mon/doc/src/book.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE book SYSTEM "book.dtd">
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header titlestyle="normal">
+ <copyright>
+ <year>1997</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>OS_Mon</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <insidecover>
+ </insidecover>
+ <pagetext>OS_Mon</pagetext>
+ <preamble>
+ <contents level="2"></contents>
+ </preamble>
+ <applications>
+ <xi:include href="ref_man.xml"/>
+ </applications>
+ <releasenotes>
+ <xi:include href="notes.xml"/>
+ </releasenotes>
+ <listofterms></listofterms>
+ <index></index>
+</book>
+
diff --git a/lib/os_mon/doc/src/cpu_sup.xml b/lib/os_mon/doc/src/cpu_sup.xml
new file mode 100644
index 0000000000..7b28083fbc
--- /dev/null
+++ b/lib/os_mon/doc/src/cpu_sup.xml
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1997</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>cpu_sup</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <module>cpu_sup</module>
+ <modulesummary>A CPU Load and CPU Utilization Supervisor Process</modulesummary>
+ <description>
+ <p><c>cpu_sup</c> is a process which supervises the CPU load
+ and CPU utilization. It is part of the OS_Mon application, see
+ <seealso marker="os_mon_app">os_mon(6)</seealso>. Available for Unix,
+ although CPU utilization values (<c>util/0,1</c>) are only
+ available for Solaris and Linux.</p>
+ <p>The load values are proportional to how long time a runnable
+ Unix process has to spend in the run queue before it is scheduled.
+ Accordingly, higher values mean more system load. The returned
+ value divided by 256 produces the figure displayed by <c>rup</c>
+ and <c>top</c>. What is displayed as 2.00 in <c>rup</c>, is
+ displayed as load up to the second mark in <c>xload</c>.</p>
+ <p>For example, <c>rup</c> displays a load of 128 as 0.50, and
+ 512 as 2.00.</p>
+ <p>If the user wants to view load values as percentage of machine
+ capacity, then this way of measuring presents a problem, because
+ the load values are not restricted to a fixed interval. In this
+ case, the following simple mathematical transformation can
+ produce the load value as a percentage:</p>
+ <code type="none">
+ PercentLoad = 100 * (1 - D/(D + Load))
+ </code>
+ <p><c>D</c> determines which load value should be associated with
+ which percentage. Choosing <c>D</c> = 50 means that 128 is 60%
+ load, 256 is 80%, 512 is 90%, and so on.</p>
+ <p>Another way of measuring system load is to divide the number of
+ busy CPU cycles by the total number of CPU cycles. This produces
+ values in the 0-100 range immediately. However, this method hides
+ the fact that a machine can be more or less saturated. CPU
+ utilization is therefore a better name than system load for this
+ measure.</p>
+ <p>A server which receives just enough requests to never become
+ idle will score a CPU utilization of 100%. If the server receives
+ 50% more requests, it will still scores 100%. When the system load
+ is calculated with the percentage formula shown previously,
+ the load will increase from 80% to 87%.</p>
+ <p>The <c>avg1/0</c>, <c>avg5/0</c>, and <c>avg15/0</c> functions
+ can be used for retrieving system load values, and
+ the <c>util/0</c> and <c>util/1</c> functions can be used for
+ retrieving CPU utilization values.</p>
+ <p>When run on Linux, <c>cpu_sup</c> assumes that the <c>/proc</c>
+ file system is present and accessible by <c>cpu_sup</c>. If it is
+ not, <c>cpu_sup</c> will terminate.</p>
+ </description>
+ <funcs>
+ <func>
+ <name>nprocs() -> UnixProcesses | {error, Reason}</name>
+ <fsummary>Get the number of UNIX processes running on this host</fsummary>
+ <type>
+ <v>UnixProcesses = int()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Returns the number of UNIX processes running on this machine.
+ This is a crude way of measuring the system load, but it may
+ be of interest in some cases.</p>
+ <p>Returns 0 if <c>cpu_sup</c> is not available.</p>
+ </desc>
+ </func>
+ <func>
+ <name>avg1() -> SystemLoad | {error, Reason}</name>
+ <fsummary>Get the system load average for the last minute</fsummary>
+ <type>
+ <v>SystemLoad = int()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Returns the average system load in the last minute, as
+ described above. 0 represents no load, 256 represents the load
+ reported as 1.00 by <c>rup</c>.</p>
+ <p>Returns 0 if <c>cpu_sup</c> is not available.</p>
+ </desc>
+ </func>
+ <func>
+ <name>avg5() -> SystemLoad | {error, Reason}</name>
+ <fsummary>Get the system load average for the last five minutes</fsummary>
+ <type>
+ <v>SystemLoad = int()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Returns the average system load in the last five minutes, as
+ described above. 0 represents no load, 256 represents the load
+ reported as 1.00 by <c>rup</c>.</p>
+ <p>Returns 0 if <c>cpu_sup</c> is not available.</p>
+ </desc>
+ </func>
+ <func>
+ <name>avg15() -> SystemLoad | {error, Reason}</name>
+ <fsummary>Get the system load average for the last fifteen minutes</fsummary>
+ <type>
+ <v>SystemLoad = int()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Returns the average system load in the last 15 minutes, as
+ described above. 0 represents no load, 256 represents the load
+ reported as 1.00 by <c>rup</c>.</p>
+ <p>Returns 0 if <c>cpu_sup</c> is not available.</p>
+ </desc>
+ </func>
+ <func>
+ <name>util() -> CpuUtil | {error, Reason}</name>
+ <fsummary>Get the CPU utilization</fsummary>
+ <type>
+ <v>CpuUtil = float()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Returns CPU utilization since the last call to
+ <c>util/0</c> or <c>util/1</c> by the calling process.</p>
+ <note>
+ <p>The returned value of the first call to <c>util/0</c> or
+ <c>util/1</c> by a process will on most systems be the CPU
+ utilization since system boot, but this is not guaranteed
+ and the value should therefore be regarded as garbage. This
+ also applies to the first call after a restart of
+ <c>cpu_sup</c>.</p>
+ </note>
+ <p>The CPU utilization is defined as the sum of the percentage
+ shares of the CPU cycles spent in all busy processor states
+ (see <c>util/1</c> below) in average on all CPUs.</p>
+ <p>Returns 0 if <c>cpu_sup</c> is not available.</p>
+ </desc>
+ </func>
+ <func>
+ <name>util(Opts) -> UtilSpec | {error, Reason}</name>
+ <fsummary>Get the CPU utilization</fsummary>
+ <type>
+ <v>Opts = [detailed | per_cpu]</v>
+ <v>UtilSpec = UtilDesc | [UtilDesc]</v>
+ <v>&nbsp;UtilDesc = {Cpus, Busy, NonBusy, Misc}</v>
+ <v>&nbsp;&nbsp;Cpus = all | int() | [int()]()</v>
+ <v>&nbsp;&nbsp;Busy = NonBusy = {State, Share} | Share</v>
+ <v>&nbsp;&nbsp;&nbsp;State = user | nice_user | kernel</v>
+ <v>&nbsp;&nbsp;&nbsp;&nbsp;| wait | idle | atom()</v>
+ <v>&nbsp;&nbsp;&nbsp;Share = float()</v>
+ <v>&nbsp;&nbsp;Misc = []</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Returns CPU utilization since the last call to
+ <c>util/0</c> or <c>util/1</c> by the calling process, in
+ more detail than <c>util/0</c>.</p>
+ <note>
+ <p>The returned value of the first call to <c>util/0</c> or
+ <c>util/1</c> by a process will on most systems be the CPU
+ utilization since system boot, but this is not guaranteed
+ and the value should therefore be regarded as garbage. This
+ also applies to the first call after a restart of
+ <c>cpu_sup</c>.</p>
+ </note>
+ <p>Currently recognized options:</p>
+ <taglist>
+ <tag><c>detailed</c></tag>
+ <item>
+ <p>The returned <c>UtilDesc</c>(s) will be even more
+ detailed.</p>
+ </item>
+ <tag><c>per_cpu</c></tag>
+ <item>
+ <p>Each CPU will be specified separately (assuming this
+ information can be retrieved from the operating system),
+ that is, a list with one <c>UtilDesc</c> per CPU will be
+ returned.</p>
+ </item>
+ </taglist>
+ <p>Description of <c>UtilDesc = {Cpus, Busy, NonBusy, Misc}</c>:</p>
+ <taglist>
+ <tag><c>Cpus</c></tag>
+ <item>
+ <p>If the <c>detailed</c> and/or <c>per_cpu</c> option is
+ given, this is the CPU number, or a list of the CPU
+ numbers.</p>
+ <p>If not, this is the atom <c>all</c> which implies that
+ the <c>UtilDesc</c> contains information about all CPUs.</p>
+ </item>
+ <tag><c>Busy</c></tag>
+ <item>
+ <p>If the <c>detailed</c> option is given, this is a list
+ of <c>{State, Share}</c> tuples, where each tuple
+ contains information about a processor state that has
+ been identified as a busy processor state (see below).
+ The atom <c>State</c> is the name of the state, and
+ the float <c>Share</c> represents the percentage share of
+ the CPU cycles spent in this state since the last call to
+ <c>util/0</c> or <c>util/1</c>.</p>
+ <p>If not, this is the sum of the percentage shares of
+ the CPU cycles spent in all states identified as busy.</p>
+ <p>If the <c>per_cpu</c> is not given, the value(s)
+ presented are the average of all CPUs.</p>
+ </item>
+ <tag><c>NonBusy</c></tag>
+ <item>
+ <p>Similar to <c>Busy</c>, but for processor states that
+ have been identified as non-busy (see below).</p>
+ </item>
+ <tag><c>Misc</c></tag>
+ <item>
+ <p>Currently unused; reserved for future use.</p>
+ </item>
+ </taglist>
+ <p>Currently these processor states are identified as busy:</p>
+ <taglist>
+ <tag><c>user</c></tag>
+ <item>
+ <p>Executing code in user mode.</p>
+ </item>
+ <tag><c>nice_user</c></tag>
+ <item>
+ <p>Executing code in low priority (nice) user mode.
+ This state is currently only identified on Linux.</p>
+ </item>
+ <tag><c>kernel</c></tag>
+ <item>
+ <p>Executing code in kernel mode.</p>
+ </item>
+ </taglist>
+ <p>Currently these processor states are identified as non-busy:</p>
+ <taglist>
+ <tag><c>wait</c></tag>
+ <item>
+ <p>Waiting. This state is currently only identified on
+ Solaris.</p>
+ </item>
+ <tag><c>idle</c></tag>
+ <item>
+ <p>Idle.</p>
+ </item>
+ </taglist>
+ <note>
+ <p>Identified processor states may be different on different
+ operating systems and may change between different versions
+ of <c>cpu_sup</c> on the same operating system. The sum of
+ the percentage shares of the CPU cycles spent in all busy
+ and all non-busy processor states will always add up to
+ 100%, though.</p>
+ </note>
+ <p>Returns <c>{all,0,0,[]}</c> if <c>cpu_sup</c> is not
+ available.</p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>See Also</title>
+ <p><seealso marker="os_mon_app">os_mon(3)</seealso></p>
+ </section>
+</erlref>
+
diff --git a/lib/os_mon/doc/src/disksup.xml b/lib/os_mon/doc/src/disksup.xml
new file mode 100644
index 0000000000..bfa3d3578f
--- /dev/null
+++ b/lib/os_mon/doc/src/disksup.xml
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1996</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>disksup</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <module>disksup</module>
+ <modulesummary>A Disk Supervisor Process</modulesummary>
+ <description>
+ <p><c>disksup</c> is a process which supervises the available disk
+ space in the system. It is part of the OS_Mon application, see
+ <seealso marker="os_mon_app">os_mon(6)</seealso>. Available for Unix
+ and Windows.</p>
+ <p>Periodically checks the disks. For each disk or partition which
+ uses more than a certain amount of the available space, the alarm
+ <c>{{disk_almost_full, MountedOn}, []}</c> is set.</p>
+ <taglist>
+ <tag>On Unix</tag>
+ <item>
+ <p>All (locally) mounted disks are checked, including the swap
+ disk if it is present.</p>
+ </item>
+ <tag>On WIN32</tag>
+ <item>
+ <p>All logical drives of type "FIXED_DISK" are checked.</p>
+ </item>
+ </taglist>
+ <p>Alarms are reported to the SASL alarm handler, see
+ <seealso marker="sasl:alarm_handler">alarm_handler(3)</seealso>.
+ To set an alarm, <c>alarm_handler:set_alarm(Alarm)</c> is called
+ where <c>Alarm</c> is the alarm specified above.</p>
+ <p>The alarms are cleared automatically when the alarm cause is no
+ longer valid.</p>
+ </description>
+
+ <section>
+ <marker id="config"></marker>
+ <title>Configuration</title>
+ <p>The following configuration parameters can be used to change
+ the default values for time interval and threshold:</p>
+ <taglist>
+ <tag><c>disk_space_check_interval = int()>0</c></tag>
+ <item>
+ <p>The time interval, in minutes, for the periodic disk space
+ check. The default is 30 minutes.</p>
+ </item>
+ <tag><c>disk_almost_full_threshold = float()</c></tag>
+ <item>
+ <p>The threshold, as percentage of total disk space, for how
+ much disk can be utilized before the <c>disk_almost_full</c>
+ alarm is set. The default is 0.80 (80%).</p>
+ </item>
+ </taglist>
+ <p>See <seealso marker="kernel:config">config(4)</seealso> for
+ information about how to change the value of configuration
+ parameters.</p>
+ </section>
+ <funcs>
+ <func>
+ <name>get_disk_data() -> [DiskData]</name>
+ <fsummary>Get data for the disks in the system</fsummary>
+ <type>
+ <v>DiskData = {Id, KByte, Capacity}</v>
+ <v>&nbsp;Id = string()</v>
+ <v>&nbsp;KByte = int()</v>
+ <v>&nbsp;Capacity = int()</v>
+ </type>
+ <desc>
+ <p>Returns the result of the latest disk check. <c>Id</c> is a
+ string that identifies the disk or partition. <c>KByte</c> is
+ the total size of the disk or partition in kbytes.
+ <c>Capacity</c> is the percentage of disk space used.</p>
+ <p>The function is asynchronous in the sense that it does not
+ invoke a disk check, but returns the latest available value.</p>
+ <p>Returns <c>[{"none",0,0}]</c> if <c>disksup</c> is not
+ available.</p>
+ </desc>
+ </func>
+ <func>
+ <name>get_check_interval() -> MS</name>
+ <fsummary>Get time interval, in milliseconds, for the periodic disk space check</fsummary>
+ <type>
+ <v>MS = int()</v>
+ </type>
+ <desc>
+ <p>Returns the time interval, in milliseconds, for the periodic
+ disk space check.</p>
+ </desc>
+ </func>
+ <func>
+ <name>set_check_interval(Minutes) -> ok</name>
+ <fsummary>Set time interval, in minutes, for the periodic disk space check</fsummary>
+ <type>
+ <v>Minutes = int()>=1</v>
+ </type>
+ <desc>
+ <p>Changes the time interval, given in minutes, for the periodic
+ disk space check.</p>
+ <p>The change will take effect after the next disk space check
+ and is non-persist. That is, in case of a process restart,
+ this value is forgotten and the default value will be used.
+ See <seealso marker="#config">Configuration</seealso> above.</p>
+ </desc>
+ </func>
+ <func>
+ <name>get_almost_full_threshold() -> Percent</name>
+ <fsummary>Get threshold, in percent, for disk space utilization</fsummary>
+ <type>
+ <v>Percent = int()</v>
+ </type>
+ <desc>
+ <p>Returns the threshold, in percent, for disk space utilization.</p>
+ </desc>
+ </func>
+ <func>
+ <name>set_almost_full_threshold(Float) -> ok</name>
+ <fsummary>Set threshold, as percentage represented by a float, for disk space utilization</fsummary>
+ <type>
+ <v>Float = float(), 0=&lt;Float=&lt;1</v>
+ </type>
+ <desc>
+ <p>Changes the threshold, given as a float, for disk space
+ utilization.</p>
+ <p>The change will take effect during the next disk space check
+ and is non-persist. That is, in case of a process restart,
+ this value is forgotten and the default value will be used.
+ See <seealso marker="#config">Configuration</seealso> above.</p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>See Also</title>
+ <p><seealso marker="sasl:alarm_handler">alarm_handler(3)</seealso>,
+ <seealso marker="os_mon_app">os_mon(3)</seealso></p>
+ </section>
+</erlref>
+
diff --git a/lib/os_mon/doc/src/fascicules.xml b/lib/os_mon/doc/src/fascicules.xml
new file mode 100644
index 0000000000..43090b4aed
--- /dev/null
+++ b/lib/os_mon/doc/src/fascicules.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
+
+<fascicules>
+ <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
+ Reference Manual
+ </fascicule>
+ <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
+ Release Notes
+ </fascicule>
+ <fascicule file="" href="../../../../doc/print.html" entry="no">
+ Off-Print
+ </fascicule>
+</fascicules>
+
diff --git a/lib/os_mon/doc/src/make.dep b/lib/os_mon/doc/src/make.dep
new file mode 100644
index 0000000000..b657f2e036
--- /dev/null
+++ b/lib/os_mon/doc/src/make.dep
@@ -0,0 +1,21 @@
+# ----------------------------------------------------
+# >>>> Do not edit this file <<<<
+# This file was automaticly generated by
+# /home/otp/bin/docdepend
+# ----------------------------------------------------
+
+
+# ----------------------------------------------------
+# TeX files that the DVI file depend on
+# ----------------------------------------------------
+
+book.dvi: book.tex cpu_sup.tex disksup.tex memsup.tex \
+ nteventlog.tex os_mon.tex os_mon_mib.tex os_sup.tex \
+ ref_man.tex
+
+# ----------------------------------------------------
+# Source inlined when transforming from source to LaTeX
+# ----------------------------------------------------
+
+book.tex: ref_man.xml
+
diff --git a/lib/os_mon/doc/src/memsup.xml b/lib/os_mon/doc/src/memsup.xml
new file mode 100644
index 0000000000..67d617375e
--- /dev/null
+++ b/lib/os_mon/doc/src/memsup.xml
@@ -0,0 +1,328 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1996</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>memsup</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <module>memsup</module>
+ <modulesummary>A Memory Supervisor Process</modulesummary>
+ <description>
+ <p><c>memsup</c> is a process which supervises the memory usage for
+ the system and for individual processes. It is part of the OS_Mon
+ application, see <seealso marker="os_mon_app">os_mon(6)</seealso>.
+ Available for Unix, Windows and VxWorks.</p>
+ <p>Periodically performs a memory check:</p>
+ <list type="bulleted">
+ <item>
+ <p>If more than a certain amount of available system memory is
+ allocated, as reported by the underlying operating system,
+ the alarm <c>{system_memory_high_watermark, []}</c> is set.</p>
+ </item>
+ <item>
+ <p>If any Erlang process <c>Pid</c> in the system has allocated
+ more than a certain amount of total system memory, the alarm
+ <c>{process_memory_high_watermark, Pid}</c> is set.</p>
+ </item>
+ </list>
+ <p>Alarms are reported to the SASL alarm handler, see
+ <seealso marker="sasl:alarm_handler">alarm_handler(3)</seealso>.
+ To set an alarm, <c>alarm_handler:set_alarm(Alarm)</c> is called
+ where <c>Alarm</c> is either of the alarms specified above.</p>
+ <p>The alarms are cleared automatically when the alarm cause is no
+ longer valid.</p>
+ <p>The function
+ <seealso marker="#get_memory_data/0">get_memory_data()</seealso>
+ can be used to retrieve the result of the latest periodic memory
+ check.</p>
+ <p>There is also a interface to system dependent memory data,
+ <seealso marker="#get_system_memory_data/0">get_system_memory_data()</seealso>.
+ The result is highly dependent on the underlying operating
+ system and the interface is targeted primarily for systems
+ without virtual memory (e.g. VxWorks). The output on other
+ systems is however still valid, although sparse.</p>
+ <p>A call to <c>get_system_memory_data/0</c> is more costly
+ than a call to <c>get_memory_data/0</c> as data is collected
+ synchronously when this function is called.</p>
+ <p>The total system memory reported under UNIX is the number of
+ physical pages of memory times the page size, and the available
+ memory is the number of available physical pages times the page
+ size. This is a reasonable measure as swapping should be avoided
+ anyway, but the task of defining total memory and available
+ memory is difficult because of virtual memory and swapping.</p>
+ </description>
+
+ <section>
+ <marker id="config"></marker>
+ <title>Configuration</title>
+ <p>The following configuration parameters can be used to change
+ the default values for time intervals and thresholds:</p>
+ <taglist>
+ <tag><c>memory_check_interval = int()>0</c></tag>
+ <item>
+ <p>The time interval, in minutes, for the periodic memory check.
+ The default is one minute.</p>
+ </item>
+ <tag><c>system_memory_high_watermark = float()</c></tag>
+ <item>
+ <p>The threshold, as percentage of system memory, for how much
+ system memory can be allocated before the corresponding alarm
+ is set. The default is 0.80 (80%).</p>
+ </item>
+ <tag><c>process_memory_high_watermark = float()</c></tag>
+ <item>
+ <p>The threshold, as percentage of system memory, for how much
+ system memory can be allocated by one Erlang process before
+ the corresponding alarm is set. The default is 0.05 (5%).</p>
+ </item>
+ <tag><c>memsup_helper_timeout = int()>0</c></tag>
+ <item>
+ <p>A timeout, in seconds, for how long the <c>memsup</c>
+ process should wait for a result from a memory check. If
+ the timeout expires, a warning message <c>"OS_MON (memsup) timeout"</c> is issued via <c>error_logger</c> and any
+ pending, synchronous client calls will return a dummy value.
+ Normally, this situation should not occur. There have been
+ cases on Linux, however, where the pseudo file from which
+ system data is read is temporarily unavailable when the system
+ is heavily loaded.</p>
+ <p>The default is 30 seconds. </p>
+ </item>
+ <tag><c>memsup_system_only = bool()</c></tag>
+ <item>
+ <p>Specifies whether the <c>memsup</c> process should only
+ check system memory usage (<c>true</c>) or not. The default is
+ <c>false</c>, meaning that information regarding both system
+ memory usage and Erlang process memory usage is collected.</p>
+ <p>It is recommended to set this parameter to <c>false</c> on
+ systems with many concurrent processes, as each process memory
+ check makes a traversal of the entire list of processes.</p>
+ </item>
+ </taglist>
+ <p>See <seealso marker="kernel:config">config(4)</seealso> for
+ information about how to change the value of configuration
+ parameters.</p>
+ </section>
+ <funcs>
+ <func>
+ <name>get_memory_data() -> {Total,Allocated,Worst}</name>
+ <fsummary>Get data for the memory in the system</fsummary>
+ <type>
+ <v>Total = Allocated = int()</v>
+ <v>Worst = {Pid, PidAllocated} | undefined</v>
+ <v>&nbsp;Pid = pid()</v>
+ <v>&nbsp;PidAllocated = int()</v>
+ </type>
+ <desc>
+ <p>Returns the result of the latest memory check, where
+ <c>Total</c> is the total memory size and <c>Allocated</c>
+ the allocated memory size, in bytes.</p>
+ <p><c>Worst</c> is the pid and number of allocated bytes of
+ the largest Erlang process on the node. If <c>memsup</c>
+ should not collect process data, that is if the configuration
+ parameter <c>memsup_system_only</c> was set to <c>true</c>,
+ <c>Worst</c> is <c>undefined</c>.</p>
+ <p>The function is normally asynchronous in the sense that it
+ does not invoke a memory check, but returns the latest
+ available value. The one exception if is the function is
+ called before a first memory check is finished, in which case
+ it does not return a value until the memory check is finished.</p>
+ <p>Returns <c>{0,0,{pid(),0}}</c> or <c>{0,0,undefined}</c> if
+ <c>memsup</c> is not available, or if all memory checks so far
+ have timed out.</p>
+ </desc>
+ </func>
+ <func>
+ <name>get_system_memory_data() -> MemDataList</name>
+ <fsummary>Get system dependent memory data</fsummary>
+ <type>
+ <v>MemDataList = [{Tag, Size}]</v>
+ <v>&nbsp;Tag = atom()</v>
+ <v>&nbsp;Size = int()</v>
+ </type>
+ <desc>
+ <p>Invokes a memory check and returns the resulting, system
+ dependent, data as a list of tagged tuples, where <c>Tag</c>
+ can be one of the following:</p>
+ <taglist>
+ <tag><c>total_memory</c></tag>
+ <item>The total amount of memory available to the Erlang emulator,
+ allocated and free. May or may not be equal to the amount
+ of memory configured in the system.</item>
+ <tag><c>free_memory</c></tag>
+ <item>The amount of free memory available to the Erlang emulator
+ for allocation.</item>
+ <tag><c>system_total_memory</c></tag>
+ <item>The amount of memory available to the whole operating
+ system. This may well be equal to <c>total_memory</c> but
+ not necessarily.</item>
+ <tag><c>largest_free</c></tag>
+ <item>The size of the largest contiguous free memory block
+ available to the Erlang emulator.</item>
+ <tag><c>number_of_free</c></tag>
+ <item>The number of free blocks available to the Erlang runtime
+ system. This gives a fair indication of how fragmented
+ the memory is.</item>
+ <tag><c>buffered_memory</c></tag>
+ <item>
+ The amount of memory the system uses for temporary storing raw disk blocks.
+ </item>
+ <tag><c>cached_memory</c></tag>
+ <item>
+ The amount of memory the system uses for cached files read from disk.
+ </item>
+ <tag><c>total_swap</c></tag>
+ <item>
+ The amount of total amount of memory the system has available
+ for disk swap.
+ </item>
+ <tag><c>free_swap</c></tag>
+ <item>
+ The amount of memory the system has available for disk swap.
+ </item>
+
+ </taglist>
+ <p>All memory sizes are presented as number of <em>bytes</em>.</p>
+ <p>The <c>largest_free</c> and <c>number_of_free</c> tags are
+ currently only returned on a VxWorks system.</p>
+ <p>Returns the empty list [] if <c>memsup</c> is not available,
+ or if the memory check times out.</p>
+ <note><p>
+ On linux the memory available to the emulator is <c>cached_memory</c> and <c>buffered_memory</c> in addition to
+ <c>free_memory</c>.</p>
+ </note>
+ </desc>
+ </func>
+ <func>
+ <name>get_os_wordsize() -> Wordsize</name>
+ <fsummary>Get the wordsize of running os.</fsummary>
+ <type>
+ <v>Wordsize = 32 | 64 | unsupported_os</v>
+ </type>
+ <desc>
+ <p>Returns the wordsize of the current running operating system. </p>
+ </desc>
+ </func>
+ <func>
+ <name>get_check_interval() -> MS</name>
+ <fsummary>Get time interval, in milliseconds, for the periodic memory check</fsummary>
+ <type>
+ <v>MS = int()</v>
+ </type>
+ <desc>
+ <p>Returns the time interval, in milliseconds, for the periodic
+ memory check.</p>
+ </desc>
+ </func>
+ <func>
+ <name>set_check_interval(Minutes) -> ok</name>
+ <fsummary>Set time interval, in minutes, for the periodic memory check</fsummary>
+ <type>
+ <v>Minutes = int()>0</v>
+ </type>
+ <desc>
+ <p>Changes the time interval, given in minutes, for the periodic
+ memory check.</p>
+ <p>The change will take effect after the next memory check and is
+ non-persistent. That is, in case of a process restart, this
+ value is forgotten and the default value will be used. See
+ <seealso marker="#config">Configuration</seealso> above.</p>
+ </desc>
+ </func>
+ <func>
+ <name>get_procmem_high_watermark() -> int()</name>
+ <fsummary>Get threshold, in percent, for process memory allocation</fsummary>
+ <desc>
+ <p>Returns the threshold, in percent, for process memory
+ allocation.</p>
+ </desc>
+ </func>
+ <func>
+ <name>set_procmem_high_watermark(Float) -> ok</name>
+ <fsummary>Set threshold, as percentage represented by a float, for process memory allocation</fsummary>
+ <desc>
+ <p>Changes the threshold, given as a float, for process memory
+ allocation.</p>
+ <p>The change will take effect during the next periodic memory
+ check and is non-persistent. That is, in case of a process
+ restart, this value is forgotten and the default value will be
+ used. See <seealso marker="#config">Configuration</seealso>
+ above.</p>
+ </desc>
+ </func>
+ <func>
+ <name>get_sysmem_high_watermark() -> int()</name>
+ <fsummary>Get threshold, in percent, for system memory allocation</fsummary>
+ <desc>
+ <p>Returns the threshold, in percent, for system memory
+ allocation.</p>
+ </desc>
+ </func>
+ <func>
+ <name>set_sysmem_high_watermark(Float) -> ok</name>
+ <fsummary>Set threshold, given as a float, for system memory allocation</fsummary>
+ <desc>
+ <p>Changes the threshold, given as a float, for system memory
+ allocation.</p>
+ <p>The change will take effect during the next periodic memory
+ check and is non-persistent. That is, in case of a process
+ restart, this value is forgotten and the default value will be
+ used. See <seealso marker="#config">Configuration</seealso>
+ above.</p>
+ </desc>
+ </func>
+ <func>
+ <name>get_helper_timeout() -> Seconds</name>
+ <fsummary>Get the timeout value, in seconds, for memory checks</fsummary>
+ <type>
+ <v>Seconds = int()</v>
+ </type>
+ <desc>
+ <p>Returns the timeout value, in seconds, for memory checks.</p>
+ </desc>
+ </func>
+ <func>
+ <name>set_helper_timeout(Seconds) -> ok</name>
+ <fsummary>Set the timeout value, in seconds, for memory checks</fsummary>
+ <type>
+ <v>Seconds = int() (>= 1)</v>
+ </type>
+ <desc>
+ <p>Changes the timeout value, given in seconds, for memory
+ checks.</p>
+ <p>The change will take effect for the next memory check and is
+ non-persistent. That is, in the case of a process restart, this
+ value is forgotten and the default value will be used. See
+ <seealso marker="#config">Configuration</seealso> above.</p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>See Also</title>
+ <p><seealso marker="sasl:alarm_handler">alarm_handler(3)</seealso>,
+ <seealso marker="os_mon_app">os_mon(3)</seealso></p>
+ </section>
+</erlref>
+
diff --git a/lib/os_mon/doc/src/note.gif b/lib/os_mon/doc/src/note.gif
new file mode 100644
index 0000000000..6fffe30419
--- /dev/null
+++ b/lib/os_mon/doc/src/note.gif
Binary files differ
diff --git a/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml
new file mode 100644
index 0000000000..1a8551f57d
--- /dev/null
+++ b/lib/os_mon/doc/src/notes.xml
@@ -0,0 +1,537 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2004</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>OS_Mon Release Notes</title>
+ <prepared>otp_appnotes</prepared>
+ <docno>nil</docno>
+ <date>nil</date>
+ <rev>nil</rev>
+ <file>notes.xml</file>
+ </header>
+ <p>This document describes the changes made to the OS_Mon application.</p>
+
+<section><title>Os_Mon 2.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Memsup did not read memory correctly on MaxOSX
+ Snowleopard. This has now been corrected. (Thanks to Joel
+ Reymont)</p>
+ <p>
+ Own Id: OTP-8211</p>
+ </item>
+ <item>
+ <p>
+ Removed unused code in <c>cpu_sup.erl</c>.</p>
+ <p>
+ Own Id: OTP-8226</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The documentation is now built with open source tools
+ (xsltproc and fop) that exists on most platforms. One
+ visible change is that the frames are removed.</p>
+ <p>
+ Own Id: OTP-8201</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Os_Mon 2.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A missing define in <c>memsup.c</c> caused a build error
+ on IRIX machines. This has now been fixed.</p>
+ <p>
+ Own Id: OTP-8094</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Os_Mon 2.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ disksup:get_disk_data/0 returned disk volume in bytes
+ instead of kbytes as stated in the documentation. The
+ problem occurred on Windows only and is now corrected.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-7741</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Os_Mon 2.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>An error in <c>memsup</c> could cause <c>os_mon</c> to
+ report erroneous memory values on windows for ranges of
+ memory between 2GB and 4GB. This have now been fixed.</p>
+ <p>
+ Own Id: OTP-7944</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Added support for dragonfly OS.</p>
+ <p>
+ Own Id: OTP-7938</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Os_Mon 2.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>The copyright notices have been updated.</p>
+ <p>
+ Own Id: OTP-7851</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Os_Mon 2.1.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>A problem with <c>OTP-OS-MON-MIB.mib</c> for 64-bit
+ environments has now been fixed. The mib has been
+ extended with 64-bit memory retrieval
+ counterparts.</p><p>In addition, a new function
+ <c>get_os_wordsize/0</c> has been added in the
+ <c>memsup</c> module</p>
+ <p>
+ Own Id: OTP-7441</p>
+ </item>
+ <item>
+ <p>An error in <c>memsup.c</c> caused the compilation to
+ crash on bsd environments. This has now been fixed.</p>
+ <p>
+ Own Id: OTP-7558</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Os_Mon 2.1.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a build error that occurred on NetBSD (Thanks to
+ Per Hedeland and Raphael Langerhorst)</p>
+ <p>
+ Own Id: OTP-7505</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Memory information in
+ <c>memsup:get_system_memory_data/0</c> now has additional
+ entries in its property list for linux.</p>
+ <p>
+ Own Id: OTP-7409 Aux Id: seq10984 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Os_Mon 2.1.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ System information retrieval on darwin platforms with
+ environments locales not conforming to the C locale
+ caused an error in <c>cpu_sup</c> resulting in process
+ termination.</p>
+ <p>
+ Own Id: OTP-7320</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Os_Mon 2.1.5</title>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ CPU utilization, on linux, is now measured via a port
+ program instead of os:cmd in erlang. This should enhance
+ performance.</p>
+ <p>
+ Own Id: OTP-7108 Aux Id: OTP-6935 </p>
+ </item>
+ </list>
+ </section>
+</section>
+
+ <section><title>Os_Mon 2.1.3</title>
+ <section><title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>
+ Extended memsup memory probing on linux to use a port
+ program to probe memory usage. This is faster then the
+ previous implementation.</p>
+ <p>
+ Own Id: OTP-6860 Aux Id: seq10616 </p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+
+ <section>
+ <title>Os_Mon 2.1.2.1</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>Minor Makefile changes.</p>
+ <p>Own Id: OTP-6689</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>OS_Mon 2.1.2</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>When the <c>memsup_system_only</c> flag was set to
+ <c>true</c>, a <c>badmatch</c> exception occurred in the
+ function <c>os_mon_mib:get_load/1</c>.</p>
+ <p>Own Id: OTP-6351 Aux Id: seq10517</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>OS_Mon 2.1.1</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Did not build on Mac OS X.</p>
+ <p>Added support for IRIX. (Thanks to Michel Urvoy and
+ Daniel Solaz.)</p>
+ <p>Own Id: OTP-6136</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p><c>disksup</c>: Now using <c>round(T*100)</c> instead of
+ <c>trunc(T*100)</c> when setting the threshold value
+ given a float <c>T</c>.</p>
+ <p>Own Id: OTP-6153</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Os_Mon 2.1</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>In 2.0, a call to <c>alarm_handler:get_alarms/0</c> was
+ introduced in <c>memsup</c> and <c>disksup</c>. This will
+ lead to problems if the default <c>alarm_handler</c>
+ event handler is not used, however, and the call has now
+ been removed. (Thanks to Serge Aleynikov for pointing
+ this out.)</p>
+ <p>Own Id: OTP-6029</p>
+ </item>
+ <item>
+ <p>A bug that in rare cases caused <c>cpu_sup</c> to crash
+ has been corrected.</p>
+ <p>Own Id: OTP-6102 Aux Id: seq10312 </p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>OS_Mon 2.0</title>
+ <p>A note on backwards compatibility: The behaviour of OS_Mon 2.0 is
+ backwards compatible under normal operation, but has changed
+ somewhat in error situations: The services do not terminate
+ and the API functions do not raise exceptions in all cases where
+ they did before. Also, in the case where a service does terminate,
+ the exit reason may be different. See below for details.</p>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>A call to a OS_Mon service (cpu_sup, disksup, ...) when
+ OS_Mon is not running, or when the service is not
+ available for the OS, or when the service is not started,
+ will no longer result in an exception. Instead a warning
+ is issued via error_logger and a dummy value is returned,
+ which one is specified in the man pages for the
+ respective service.</p>
+ <p>The reason is that it should not be necessary for a
+ service to be started on each and every node of a
+ distributed Erlang system for the OS-MON-MIB and other
+ OS_Mon users to work properly.</p>
+ <p>Own Id: OTP-4332 Aux Id: seq7358 </p>
+ </item>
+ <item>
+ <p>References to the obsolete EVA application in
+ OTP-OS-MON-MIB has been removed.</p>
+ <p>Own Id: OTP-5699</p>
+ </item>
+ <item>
+ <p>Setting the option <c>memsup_system_only</c> to
+ <c>true</c> did not work, but would crash the
+ <c>memsup</c> process.</p>
+ <p>Own Id: OTP-5890 Aux Id: seq10185 </p>
+ </item>
+ <item>
+ <p><c>cpu_sup:nprocs/0</c> returned 0 on FreeBsd.</p>
+ <p>Own Id: OTP-5901</p>
+ </item>
+ <item>
+ <p>If the OS_Mon service <c>disksup</c> or <c>memsup</c> was
+ restarted, the same alarm could be set twice. Also, set
+ alarms were not cleared when the application was stopped.</p>
+ <p>Own Id: OTP-5910</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>Thresholds and time intervals in <c>disksup</c> and
+ <c>memsup</c> are now configurable in run-time.</p>
+ <p>Own Id: OTP-4246 Aux Id: Seq7230 </p>
+ </item>
+ <item>
+ <p><c>memsup</c> can now handle systems with more than 4GB
+ of RAM.</p>
+ <p>Own Id: OTP-5800 Aux Id: seq10130 </p>
+ </item>
+ <item>
+ <p>The entire OS_Mon application (code and documentation)
+ has been reviewed and consequently updated with the goal
+ to make the application more robust, homogeneous and
+ easier to configure.</p>
+ <p>The behaviour under normal operation is backwards
+ compatible. However, recoverable errors now no longer
+ terminate the affected service (and thus possible the
+ entire application), instead <c>error_logger</c> is used
+ to warn the user if/when such errors occurs. Also, in the
+ case of unrecoverable errors, the services have been made
+ more homogeneous with respect to behavior and exit
+ reasons. See below for more information and refer to the
+ man pages for details.</p>
+ <p>Port handling has been unified, meaning that if a port
+ program sends garbage or unexpectedly dies, this is now
+ handled the same way by all OS_Mon services, namely
+ immediate process termination with the exit reason
+ <c>{port_error,Garbage}</c> or <c>{port_died,Reason}</c>,
+ respectively.</p>
+ <p>Application configuration parameter handling has been
+ unified. Bad parameter values are no longer silently
+ ignored (<c>disksup</c>) or cause application termination
+ (<c>memsup</c>, <c>os_sup</c>). Instead a warning is
+ issued and the default value for the parameter is used.
+ Also, some cases where a bad parameter value accidentally
+ could be accepted have been corrected.</p>
+ <p>Message handling has been unified. Unknown
+ (<c>gen_server-</c>) calls cause process termination,
+ whereas unknown casts and messages are now ignored by all
+ OS_Mon services.</p>
+ <p>Own Id: OTP-5897</p>
+ </item>
+ <item>
+ <p>The following changes have been made to the <c>os_sup</c>
+ service:</p>
+ <p>It is now available on Windows, using <c>nteventlog</c>
+ as backend.</p>
+ <p>On Solaris, enabling the service (that is, installing a
+ new configuration file for <c>syslogd</c> etc.) can now
+ be done outside the <c>os_sup</c> process. The reason for
+ this is that the Erlang emulator should normally not be
+ run with <c>root</c> privileges, as is otherwise
+ required. The new application configuration parameter
+ <c>os_sup_config</c> must be set to <c>false</c>.</p>
+ <p>Also, <c>os_sup</c> can now be configured using a new
+ configuration parameter <c>os_sup_mfa</c> to call an
+ arbitrary Erlang function when a message is received from
+ the OS.</p>
+ <p>Own Id: OTP-5925</p>
+ </item>
+ <item>
+ <p>The <c>memsup</c> service has been rewritten, replacing
+ the supervised <c>memsup_helper</c> with a linked help
+ process. This gives the <c>memsup</c> process more
+ control and prevents a situation where it gets out of
+ synch with the received memory data and thus possibly
+ returns erroneous results.</p>
+ <p>Own Id: OTP-5927</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>OS_Mon 1.8.1</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p><c>cpu_sup:util/0</c> failed with error reason
+ <c>negative_diff</c> when called the first time on a
+ machine (hw) that had been up for a very long time.</p>
+ <p>Own Id: OTP-5869 Aux Id: seq10166</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>OS_Mon 1.8</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>The memsup part of the OS_Mon application has been made
+ more stable. If there are (possibly temporary) problems
+ collecting memory data, the interface functions
+ (<c>get_memory_data/0</c>,
+ <c>get_system_memory_data/0</c>) now do not fail, but
+ return the previously collected value, if any, or a dummy
+ value otherwise. Also, a warning message is printed.</p>
+ <p>*** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>Own Id: OTP-5798</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>OS_Mon 1.7.4</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Corrected several problems in the error handling/error
+ recovery (especially when OS_Mon is starting up).</p>
+ <p>Own Id: OTP-5559</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>OS_Mon 1.7.3</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p><c>memsup.c</c> will now compile on OpenBSD. (Thanks to
+ Geoff White and Jay Nelson.)</p>
+ <p>The <c>disksup</c> and <c>cpu_sup</c> modules now work on
+ Mac OS X (tested on Mac OS 10.3.8).</p>
+ <p>The <c>memsup</c> module should now work on Linux 2.6.* as
+ well as on older Linuxes. (<c>/proc/meminfo</c> has
+ slightly different formats in different releases of
+ Linux.)</p>
+ <p>Own Id: OTP-5421 <br></br>
+
+ Aux Id: OTP-5194, OTP-5228, OTP-5291 </p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>OS_Mon 1.7.2</title>
+ <p>This version is identical with 1.7.</p>
+ </section>
+</chapter>
+
diff --git a/lib/os_mon/doc/src/nteventlog.xml b/lib/os_mon/doc/src/nteventlog.xml
new file mode 100644
index 0000000000..1e3fad90cd
--- /dev/null
+++ b/lib/os_mon/doc/src/nteventlog.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1998</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>nteventlog</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <module>nteventlog</module>
+ <modulesummary>Interface to Windows Event Log</modulesummary>
+ <description>
+ <p><c>nteventlog</c> provides a generic interface to the Windows
+ event log. It is part of the OS_Mon application, see
+ <seealso marker="os_mon_app">os_mon(6)</seealso>. Available for
+ Windows versions where the event log is available. That is, not
+ for Windows 98 and some other older Windows versions, but for most
+ (all?) newer Windows versions.</p>
+ <p>This module is used as the Windows backend for <c>os_sup</c>, see
+ <seealso marker="os_sup">os_sup(3)</seealso>.</p>
+ <p>To retain backwards compatibility, this module can also be used
+ to start a standalone <c>nteventlog</c> process which is not part
+ of the OS_Mon supervision tree. When starting such a process,
+ the user has to supply an identifier as well as a callback
+ function to handle the messages.</p>
+ <p>The identifier, an arbitrary string, should be reused whenever
+ the same application (or node) wants to start the process.
+ <c>nteventlog</c> is informed about all events that have arrived
+ to the eventlog since the last accepted message for the current
+ identifier. As long as the same identifier is used, the same
+ eventlog record will not be sent to <c>nteventlog</c> more than
+ once (with the exception of when graved system failures arise, in
+ which case the last records written before the failure may be
+ sent to Erlang again after reboot).</p>
+ <p>If the event log is configured to wrap around automatically,
+ records that have arrived to the log and been overwritten when
+ <c>nteventlog</c> was not running are lost. It however detects
+ this state and loses no records that are not overwritten.</p>
+ <p>The callback function works as described in <c>os_sup(3)</c>.</p>
+ </description>
+ <funcs>
+ <func>
+ <name>start(Identifier, MFA) -> Result</name>
+ <name>start_link(Identifier, MFA) -> Result</name>
+ <fsummary>Start the NT eventlog server</fsummary>
+ <type>
+ <v>Identifier = string() | atom()</v>
+ <v>MFA = {Mod, Func, Args}</v>
+ <v>&nbsp;Mod = Func = atom()</v>
+ <v>&nbsp;Args = [term()]</v>
+ <v>Result = {ok, Pid} | {error, {already_started, Pid}}</v>
+ <v>Pid = pid()</v>
+ </type>
+ <desc>
+ <p>This function starts the standalone <c>nteventlog</c> process
+ and, if <c>start_link/2</c> is used, links to it.</p>
+ <p><c>Identifier</c> is an identifier as described above.</p>
+ <p><c>MFA</c> is the supplied callback function. When
+ <c>nteventlog</c> receives information about a new event, this
+ function will be called as <c>apply(Mod, Func, [Event|Args])</c> where <c>Event</c> is a tuple</p>
+ </desc>
+ </func>
+ <func>
+ <name>stop() -> stopped</name>
+ <fsummary>Stop the NT eventlog server</fsummary>
+ <type>
+ <v>Result = stopped</v>
+ </type>
+ <desc>
+ <p>Stops <c>nteventlog</c>. Usually only used during
+ development. The server does not have to be shut down
+ gracefully to maintain its state.</p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>See Also</title>
+ <p><seealso marker="os_mon_app">os_mon(6)</seealso>,
+ <seealso marker="os_sup">os_sup(3)</seealso></p>
+ <p>Windows NT documentation</p>
+ </section>
+</erlref>
+
diff --git a/lib/os_mon/doc/src/os_mon_app.xml b/lib/os_mon/doc/src/os_mon_app.xml
new file mode 100644
index 0000000000..8b44b70c5f
--- /dev/null
+++ b/lib/os_mon/doc/src/os_mon_app.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE appref SYSTEM "appref.dtd">
+
+<appref>
+ <header>
+ <copyright>
+ <year>1996</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>os_mon</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <app>os_mon</app>
+ <appsummary>OS Monitoring Application</appsummary>
+ <description>
+ <p>The operating system monitor, OS_Mon, provides the following
+ services:</p>
+ <list type="bulleted">
+ <item><seealso marker="cpu_sup">cpu_sup</seealso>
+ CPU load and utilization supervision (Unix)</item>
+ <item><seealso marker="disksup">disksup</seealso>
+ Disk supervision(Unix, Windows)</item>
+ <item><seealso marker="memsup">memsup</seealso>
+ Memory supervision (Unix, Windows, VxWorks)</item>
+ <item><seealso marker="os_sup">os_sup</seealso>
+ Interface to OS system messages (Solaris, Windows)</item>
+ </list>
+ <p>To simplify usage of OS_Mon on distributed Erlang systems, it is
+ not considered an error trying to use a service at a node where it
+ is not available (either because OS_Mon is not running, or because
+ the service is not available for that OS, or because the service
+ is not started). Instead, a warning message is issued via
+ <c>error_logger</c> and a dummy value is returned, which one is
+ specified in the man pages for the respective services.</p>
+ </description>
+
+ <section>
+ <title>Configuration</title>
+ <p>When OS_Mon is started, by default all services available for
+ the OS, except <c>os_sup</c>, are automatically started. This
+ configuration can be changed using the following application
+ configuration parameters:</p>
+ <taglist>
+ <tag><c>start_cpu_sup = bool()</c></tag>
+ <item>
+ <p>Specifies if <c>cpu_sup</c> should be started. Defaults to
+ <c>true</c>.</p>
+ </item>
+ <tag><c>start_disksup = bool()</c></tag>
+ <item>
+ <p>Specifies if <c>disksup</c> should be started. Defaults to
+ <c>true</c>.</p>
+ </item>
+ <tag><c>start_memsup = bool()</c></tag>
+ <item>
+ <p>Specifies if <c>memsup</c> should be started. Defaults to
+ <c>true</c>.</p>
+ </item>
+ <tag><c>start_os_sup = bool()</c></tag>
+ <item>
+ <p>Specifies if <c>os_sup</c> should be started. Defaults to
+ <c>false</c>.</p>
+ </item>
+ </taglist>
+ <p>Configuration parameters effecting the different OS_Mon services
+ are described in the respective man pages.</p>
+ <p>See <seealso marker="kernel:config">config(4)</seealso> for
+ information about how to change the value of configuration
+ parameters.</p>
+ </section>
+
+ <section>
+ <title>SNMP MIBs</title>
+ <p>The following MIBs are defined in OS_Mon:</p>
+ <taglist>
+ <tag>OTP-OS-MON-MIB</tag>
+ <item>
+ <p>This MIB contains objects for instrumentation of disk,
+ memory and CPU usage of the nodes in the system.</p>
+ </item>
+ </taglist>
+ <p>The MIB is stored in the <c>mibs</c> directory. It is defined
+ in SNMPv2 SMI syntax. An SNMPv1 version of the MIB is delivered
+ in the <c>mibs/v1</c> directory.</p>
+ <p>The compiled MIB is located under <c>priv/mibs</c>, and
+ the generated <c>.hrl</c> file under the <c>include</c> directory.
+ To compile a MIB that IMPORTS the <c>OTP-OS-MON-MIB</c>, give
+ the option <c>{il, ["os_mon/priv/mibs"]}</c> to the MIB compiler.</p>
+ <p>If the MIB should be used in a system, it should be loaded into
+ an agent with a call to <c>os_mon_mib:load(Agent)</c>, where
+ <c>Agent</c> is the pid or registered name of an SNMP agent. Use
+ <c>os_mon_mib:unload(Agent)</c> to unload the MIB.
+ The implementation of this MIB uses Mnesia to store a cache with
+ data needed, which implicates that Mnesia must be up and running.
+ The MIB also use functions defined for the <c>OTP-MIB</c>, thus
+ that MIB must be loaded as well.</p>
+ </section>
+
+ <section>
+ <title>See Also</title>
+ <p><seealso marker="cpu_sup">cpu_sup(3)</seealso>,
+ <seealso marker="disksup">disksup(3)</seealso>,
+ <seealso marker="memsup">memsup(3)</seealso>,
+ <seealso marker="os_sup">os_sup(3)</seealso>,
+ <seealso marker="nteventlog">nteventlog(3)</seealso>,
+ <seealso marker="snmp:snmp">snmp(3)</seealso>.</p>
+ </section>
+</appref>
+
diff --git a/lib/os_mon/doc/src/os_mon_mib.xml b/lib/os_mon/doc/src/os_mon_mib.xml
new file mode 100644
index 0000000000..220d6a4a22
--- /dev/null
+++ b/lib/os_mon/doc/src/os_mon_mib.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2004</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>os_mon_mib</title>
+ <prepared>Ingela Andin</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <module>os_mon_mib</module>
+ <modulesummary>Loading and Unloading of OTP-OS-MON-MIB</modulesummary>
+ <description>
+ <p>Functions for loading and unloading the OTP-OS-MON-MIB into/from
+ an SNMP agent. The instrumentation of the OTP-OS-MON-MIB uses
+ Mnesia, hence Mnesia must be started prior to loading
+ the OTP-OS-MON-MIB.</p>
+ </description>
+ <funcs>
+ <func>
+ <name>load(Agent) -> ok | {error, Reason}</name>
+ <fsummary>Load the OTP-OS-MON-MIB</fsummary>
+ <type>
+ <v>Agent = pid() | atom()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Loads the OTP-OS-MON-MIB.</p>
+ </desc>
+ </func>
+ <func>
+ <name>unload(Agent) -> ok | {error, Reason}</name>
+ <fsummary>Unload the OTP-OS-MON-MIB</fsummary>
+ <type>
+ <v>Agent = pid() | atom() </v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Unloads the OTP-OS-MON-MIB.</p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>See Also</title>
+ <p><seealso marker="os_mon_app">os_mon(6)</seealso>,
+ <seealso marker="snmp:snmp">snmp(3)</seealso></p>
+ </section>
+</erlref>
+
diff --git a/lib/os_mon/doc/src/os_sup.xml b/lib/os_mon/doc/src/os_sup.xml
new file mode 100644
index 0000000000..792d18ba14
--- /dev/null
+++ b/lib/os_mon/doc/src/os_sup.xml
@@ -0,0 +1,241 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1996</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>os_sup</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <module>os_sup</module>
+ <modulesummary>Interface to OS System Messages</modulesummary>
+ <description>
+ <p><c>os_sup</c> is a process providing a message passing service
+ from the operating system to the error logger in the Erlang
+ runtime system. It is part of the OS_Mon application, see
+ <seealso marker="os_mon_app">os_mon(6)</seealso>. Available for
+ Solaris and Windows.</p>
+ <p>Messages received from the operating system results in an
+ user defined callback function being called. This function can do
+ whatever filtering and formatting is necessary and then deploy any
+ type of logging suitable for the user's application.</p>
+ </description>
+
+ <section>
+ <title>Solaris Operation</title>
+ <p>The Solaris (SunOS 5.x) messages are retrieved from
+ the syslog-daemon, <c>syslogd</c>.</p>
+ <p>Enabling the service includes actions which require root
+ privileges, such as change of ownership and file privileges of an
+ executable binary file, and creating a modified copy of
+ the configuration file for <c>syslogd</c>. When <c>os_sup</c> is
+ terminated, the service must be disabled, meaning the original
+ configuration must be restored. Enabling/disabling can be done
+ either outside or inside <c>os_sup</c>, see
+ <seealso marker="#config">Configuration</seealso> below.</p>
+ <warning>
+ <p>This process cannot run in multiple instances on the same
+ hardware. OS_Mon must be configured to start <c>os_sup</c> on
+ one node only if two or more Erlang nodes execute on the same
+ machine.</p>
+ </warning>
+ <p>The format of received events is not defined.</p>
+ </section>
+
+ <section>
+ <title>Windows Operation</title>
+ <p>The Windows messages are retrieved from the eventlog file.</p>
+ <p>The <c>nteventlog</c> module is used to implement <c>os_sup</c>.
+ See <seealso marker="nteventlog">nteventlog(3)</seealso>. Note
+ that the start functions of <c>nteventlog</c> does not need to be
+ used, in this case the process is started automatically as part of
+ the OS_Mon supervision tree.</p>
+ <p>OS messages are formatted as a tuple
+ <c>{Time, Category, Facility, Severity, Message}</c>:</p>
+ <taglist>
+ <tag><c>Time = {MegaSecs, Secs, MicroSecs}</c></tag>
+ <item>
+ <p>A time stamp as returned by the BIF <c>now()</c>.</p>
+ </item>
+ <tag><c>Category = string()</c></tag>
+ <item>
+ <p>Usually one of <c>"System"</c>, <c>"Application"</c> or
+ <c>"Security"</c>. Note that the NT eventlog viewer has
+ another notion of category, which in most cases is totally
+ meaningless and therefore not imported into Erlang. What is
+ called a category here is one of the main three types of
+ events occurring in a normal NT system.</p>
+ </item>
+ <tag><c>Facility = string()</c></tag>
+ <item>
+ <p>The source of the message, usually the name of
+ the application that generated it. This could be almost any
+ string. When matching messages from certain applications,
+ the version number of the application may have to be
+ accounted for. This is what the NT event viewer calls
+ "source".</p>
+ </item>
+ <tag><c>Severity = string()</c></tag>
+ <item>
+ <p>One of <c>"Error"</c>, <c>"Warning"</c>,
+ <c>"Informational"</c>, <c>"Audit_Success"</c>,
+ <c>"Audit_Faulure"</c> or, in case of a currently unknown
+ Windows NT version <c>"Severity_Unknown"</c>.</p>
+ </item>
+ <tag><c>Message = string()</c></tag>
+ <item>
+ <p>Formatted exactly as it would be in the NT eventlog viewer.
+ Binary data is not imported into Erlang.</p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <marker id="config"></marker>
+ <title>Configuration</title>
+ <taglist>
+ <tag><c>os_sup_mfa = {Module, Function, Args}</c></tag>
+ <item>
+ <p>The callback function to use. <c>Module</c> and
+ <c>Function</c> are atoms and <c>Args</c> is a list of terms.
+ When an OS message <c>Msg</c> is received, this function is
+ called as <c>apply(Module, Function, [Msg | Args])</c>.</p>
+ <p>Default is <c>{os_sup, error_report, [Tag]}</c> which will
+ send the event to the error logger using
+ <seealso marker="kernel:error_logger#error_report/2">error_logger:error_report(Tag, Msg)</seealso>. <c>Tag</c> is the value of
+ <c>os_sup_errortag</c>, see below.</p>
+ </item>
+ <tag><c>os_sup_errortag = atom()</c></tag>
+ <item>
+ <p>This parameter defines the error report type used when
+ messages are sent to error logger using the default callback
+ function. Default is <c>std_error</c>, which means the events
+ are handled by the standard event handler.</p>
+ </item>
+ <tag><c>os_sup_enable = bool()</c></tag>
+ <item>
+ <p>Solaris only. Defines if the service should be enabled (and
+ disabled) inside (<c>true</c>) or outside (<c>false</c>)
+ <c>os_sup</c>. For backwards compatibility reasons,
+ the default is <c>true</c>. The recommended value is
+ <c>false</c>, as the Erlang emulator should normally not be
+ run with <c>root</c> privileges, as is required for enabling
+ the service.</p>
+ </item>
+ <tag><c>os_sup_own = string()</c></tag>
+ <item>
+ <p>Solaris only. Defines the directory which contains
+ the backup copy and the Erlang specific configuration files
+ for <c>syslogd</c>, and a named pipe to receive the messages
+ from <c>syslogd</c>. Default is <c>"/etc"</c>.</p>
+ </item>
+ <tag><c>os_sup_syslogconf = string()</c></tag>
+ <item>
+ <p>Solaris only. Defines the full name of the configuration file
+ for <c>syslogd</c>. Default is <c>"/etc/syslog.conf"</c>.</p>
+ </item>
+ </taglist>
+ </section>
+ <funcs>
+ <func>
+ <name>enable() -> ok | {error, Res}</name>
+ <name>enable(Dir, Conf) -> ok | {error, Error}</name>
+ <fsummary>Enable the service (Solaris only)</fsummary>
+ <type>
+ <v>Dir = Conf = Res = string()</v>
+ </type>
+ <desc>
+ <p>Enables the <c>os_sup</c> service. Needed on Solaris only.</p>
+ <p>If the configuration parameter <c>os_sup_enable</c> is
+ <c>false</c>, this function is called automatically by
+ <c>os_sup</c>, using the values of <c>os_sup_own</c> and
+ <c>os_sup_syslogconf</c> as arguments.</p>
+ <p>If <c>os_sup_enable</c> is <c>true</c>, this function must
+ be called <em>before</em> OS_Mon/<c>os_sup</c> is started.
+ <c>Dir</c> defines the directory which contains the backup
+ copy and the Erlang specific configuration files for
+ <c>syslogd</c>, and a named pipe to receive the messages
+ from <c>syslogd</c>. Defaults to <c>"/etc"</c>. <c>Conf</c>
+ defines the full name of the configuration file for
+ <c>syslogd</c>. Default is <c>"/etc/syslog.conf"</c>.</p>
+ <p>Results in a OS call to:</p>
+ <code type="none"><![CDATA[
+<PRIVDIR>/bin/mod_syslog otp Dir Conf
+ ]]></code>
+ <p>where <c><![CDATA[<PRIVDIR>]]></c> is the <c>priv</c> directory of
+ OS_Mon, <c>code:priv_dir(os_mon)</c>.</p>
+ <p>Returns <c>ok</c> if this yields the expected result
+ <c>"0"</c>, and <c>{error, Res}</c> if it yields anything
+ else.</p>
+ <note>
+ <p>This function requires root privileges to succeed.</p>
+ </note>
+ </desc>
+ </func>
+ <func>
+ <name>disable() -> ok | {error, Res}</name>
+ <name>disable(Dir, Conf) -> ok | {error, Error}</name>
+ <fsummary>Disable the service (Solaris only)</fsummary>
+ <type>
+ <v>Dir = Conf = Res = string()</v>
+ </type>
+ <desc>
+ <p>Disables the <c>os_sup</c> service. Needed on Solaris only.</p>
+ <p>If the configuration parameter <c>os_sup_enable</c> is
+ <c>false</c>, this function is called automatically by
+ <c>os_sup</c>, using the same arguments as when
+ <c>enable/2</c> was called.</p>
+ <p>If <c>os_sup_enable</c> is <c>true</c>, this function must
+ be called <em>after</em> OS_Mon/<c>os_sup</c> is stopped.
+ <c>Dir</c> defines the directory which contains the backup
+ copy and the Erlang specific configuration files for
+ <c>syslogd</c>, and a named pipe to receive the messages
+ from <c>syslogd</c>. Defaults to <c>"/etc"</c>. <c>Conf</c>
+ defines the full name of the configuration file for
+ <c>syslogd</c>. Default is <c>"/etc/syslog.conf"</c>.</p>
+ <p>Results in a OS call to:</p>
+ <code type="none"><![CDATA[
+<PRIVDIR>/bin/mod_syslog nootp Dir Conf
+ ]]></code>
+ <p>where <c><![CDATA[<PRIVDIR>]]></c> is the <c>priv</c> directory of
+ OS_Mon, <c>code:priv_dir(os_mon)</c>.</p>
+ <p>Returns <c>ok</c> if this yields the expected result
+ <c>"0"</c>, and <c>{error, Res}</c> if it yields anything
+ else.</p>
+ <note>
+ <p>This function requires root privileges to succeed.</p>
+ </note>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>See also</title>
+ <p><seealso marker="kernel:error_logger">error_logger(3)</seealso>,
+ <seealso marker="os_mon_app">os_mon(3)</seealso></p>
+ <p><c>syslogd(1M)</c>, <c>syslog.conf(4)</c> in the Solaris
+ documentation.</p>
+ <p></p>
+ </section>
+</erlref>
+
diff --git a/lib/os_mon/doc/src/part_notes.xml b/lib/os_mon/doc/src/part_notes.xml
new file mode 100644
index 0000000000..8fe226f53e
--- /dev/null
+++ b/lib/os_mon/doc/src/part_notes.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2004</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>OS_Mon Release Notes</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <description>
+ <p>The operating system monitor, OS_Mon, provides services for
+ monitoring CPU load, disk usage, memory usage and OS messages.</p>
+ </description>
+ <xi:include href="notes.xml"/>
+</part>
+
diff --git a/lib/os_mon/doc/src/ref_man.xml b/lib/os_mon/doc/src/ref_man.xml
new file mode 100644
index 0000000000..5a50c122fd
--- /dev/null
+++ b/lib/os_mon/doc/src/ref_man.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE application SYSTEM "application.dtd">
+
+<application xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>1996</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>OS_Mon Reference Manual</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <description>
+ <p>The operating system monitor, OS_Mon, provides services for
+ monitoring CPU load, disk usage, memory usage and OS messages.</p>
+ </description>
+ <xi:include href="os_mon_app.xml"/>
+ <xi:include href="cpu_sup.xml"/>
+ <xi:include href="disksup.xml"/>
+ <xi:include href="memsup.xml"/>
+ <xi:include href="os_mon_mib.xml"/>
+ <xi:include href="os_sup.xml"/>
+ <xi:include href="nteventlog.xml"/>
+</application>
+
diff --git a/lib/os_mon/doc/src/user_guide.gif b/lib/os_mon/doc/src/user_guide.gif
new file mode 100644
index 0000000000..e6275a803d
--- /dev/null
+++ b/lib/os_mon/doc/src/user_guide.gif
Binary files differ
diff --git a/lib/os_mon/doc/src/warning.gif b/lib/os_mon/doc/src/warning.gif
new file mode 100644
index 0000000000..96af52360e
--- /dev/null
+++ b/lib/os_mon/doc/src/warning.gif
Binary files differ
diff --git a/lib/os_mon/ebin/.gitignore b/lib/os_mon/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/os_mon/ebin/.gitignore
diff --git a/lib/os_mon/include/memsup.hrl b/lib/os_mon/include/memsup.hrl
new file mode 100644
index 0000000000..743d16522e
--- /dev/null
+++ b/lib/os_mon/include/memsup.hrl
@@ -0,0 +1,43 @@
+%%
+%% %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(_memsup_hrl).
+-define(_memsup_hrl,true).
+%%% This file has to be kept consistent with ../c_src/memsup.h.
+%%% Keep consistence manually.
+
+%% Defines
+
+-define( SHOW_MEM , 1 ).
+-define( SHOW_SYSTEM_MEM , 2 ).
+-define( SHOW_SYSTEM_MEM_END , 8#0 ).
+%% tags for extended statistics
+-define( MEM_SYSTEM_TOTAL , 1 ).
+-define( MEM_TOTAL , 2 ).
+-define( MEM_FREE , 3 ).
+-define( MEM_LARGEST_FREE , 4 ).
+-define( MEM_NUMBER_OF_FREE , 5 ).
+%% extensions
+-define( MEM_BUFFERS , 6 ).
+-define( MEM_CACHED , 7 ).
+-define( MEM_SHARED , 8 ).
+-define( SWAP_TOTAL , 9 ).
+-define( SWAP_FREE , 10 ).
+
+-endif.
+
diff --git a/lib/os_mon/info b/lib/os_mon/info
new file mode 100644
index 0000000000..bc3f971c2a
--- /dev/null
+++ b/lib/os_mon/info
@@ -0,0 +1,3 @@
+group: oam
+short: A monitor which allows inspection of the underlying operating system
+
diff --git a/lib/os_mon/mibs/Makefile b/lib/os_mon/mibs/Makefile
new file mode 100644
index 0000000000..cbbc337491
--- /dev/null
+++ b/lib/os_mon/mibs/Makefile
@@ -0,0 +1,97 @@
+#
+# %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
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(OS_MON_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/os_mon-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MIB_FILES= OTP-OS-MON-MIB.mib
+FUNCS_FILES = OTP-OS-MON-MIB.funcs
+
+BIN_TARGETS= $(MIB_FILES:%.mib=$(SNMP_BIN_TARGET_DIR)/%.bin)
+HRL_TARGETS= $(MIB_FILES:%.mib=$(SNMP_HRL_TARGET_DIR)/%.hrl)
+V1_MIB_FILES= $(MIB_FILES:%.mib=v1/%.mib.v1)
+
+TARGET_FILES= $(SNMP_BIN_TARGET_DIR)/OTP-REG.bin \
+ $(SNMP_BIN_TARGET_DIR)/OTP-TC.bin \
+ $(SNMP_BIN_TARGET_DIR)/OTP-MIB.bin \
+ $(BIN_TARGETS) $(HRL_TARGETS) $(V1_MIB_FILES)
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+SNMP_FLAGS = -I $(SNMP_BIN_TARGET_DIR)
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+OTP_MIBDIR = $(shell if test -d ../../otp_mibs; then echo otp_mibs; \
+ else echo sasl; fi)
+
+$(SNMP_BIN_TARGET_DIR)/OTP-REG.bin: $(ERL_TOP)/lib/$(OTP_MIBDIR)/mibs/OTP-REG.mib
+ $(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) -o $(SNMP_BIN_TARGET_DIR) $<
+
+$(SNMP_BIN_TARGET_DIR)/OTP-TC.bin: $(ERL_TOP)/lib/$(OTP_MIBDIR)/mibs/OTP-TC.mib
+ $(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) -o $(SNMP_BIN_TARGET_DIR) $<
+
+$(SNMP_BIN_TARGET_DIR)/OTP-MIB.bin: $(ERL_TOP)/lib/$(OTP_MIBDIR)/mibs/OTP-MIB.mib
+ $(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) -o $(SNMP_BIN_TARGET_DIR) $<
+
+v1/%.mib.v1: %.mib
+ $(ERL_TOP)/lib/snmp/bin/snmp-v2tov1 -o $@ $<
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/mibs
+ $(INSTALL_DIR) $(RELSYSDIR)/mibs/v1
+ $(INSTALL_DATA) $(MIB_FILES) $(FUNCS_FILES) $(RELSYSDIR)/mibs
+ $(INSTALL_DATA) $(V1_MIB_FILES) $(RELSYSDIR)/mibs/v1
+ $(INSTALL_DIR) $(RELSYSDIR)/include
+ $(INSTALL_DATA) $(HRL_TARGETS) $(RELSYSDIR)/include
+ $(INSTALL_DIR) $(RELSYSDIR)/priv/mibs
+ $(INSTALL_DATA) $(BIN_TARGETS) $(RELSYSDIR)/priv/mibs
+
+release_docs_spec:
diff --git a/lib/os_mon/mibs/OTP-OS-MON-MIB.funcs b/lib/os_mon/mibs/OTP-OS-MON-MIB.funcs
new file mode 100644
index 0000000000..7ed76517b9
--- /dev/null
+++ b/lib/os_mon/mibs/OTP-OS-MON-MIB.funcs
@@ -0,0 +1,5 @@
+{loadMemorySystemWatermark, {os_mon_mib, mem_sys_mark, []}}.
+{loadMemoryErlProcWatermark, {os_mon_mib, mem_proc_mark, []}}.
+{loadTable, {os_mon_mib, load_table, []}}.
+{diskAlmostFullThreshold, {os_mon_mib, disk_threshold, []}}.
+{diskTable, {os_mon_mib, disk_table, []}}.
diff --git a/lib/os_mon/mibs/OTP-OS-MON-MIB.mib b/lib/os_mon/mibs/OTP-OS-MON-MIB.mib
new file mode 100644
index 0000000000..d6ababdcea
--- /dev/null
+++ b/lib/os_mon/mibs/OTP-OS-MON-MIB.mib
@@ -0,0 +1,422 @@
+--
+-- %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%
+--
+
+OTP-OS-MON-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ MODULE-IDENTITY, NOTIFICATION-TYPE, OBJECT-TYPE,
+ Counter32, Gauge32, Integer32, Unsigned32, Counter64
+ FROM SNMPv2-SMI
+ TEXTUAL-CONVENTION, DisplayString
+ FROM SNMPv2-TC
+ MODULE-COMPLIANCE, NOTIFICATION-GROUP, OBJECT-GROUP
+ FROM SNMPv2-CONF
+ otpModules, otpApplications
+ FROM OTP-REG
+ erlNodeId
+ FROM OTP-MIB
+ ;
+
+
+otpOsMonModule MODULE-IDENTITY
+ LAST-UPDATED "0305090900Z"
+ ORGANIZATION "Ericsson"
+ CONTACT-INFO
+ "Contact: Erlang Support see license agreement for Erlang/OTP."
+
+ DESCRIPTION
+ "This MIB is part of the OTP MIB. It defines MIB objects
+ for the os_mon application in OTP."
+
+ REVISION "0508260900Z"
+ DESCRIPTION
+ "Removed dependeny on EVA."
+ REVISION "0305090900Z"
+ DESCRIPTION
+ "Changed CONTACT-INFO as it was outdated, made it more generic
+ to avoid such changes in the future."
+
+ REVISION "9807080900Z"
+ DESCRIPTION
+ "Changed MAX-ACCESS for diskDescr from not-accessible to
+ read-only."
+
+ REVISION "9801270900Z"
+ DESCRIPTION
+ "Changed erroneous name of this module to otpOsMonModule."
+
+ REVISION "9712010900Z"
+ DESCRIPTION
+ "Converted to v2 SMI and placed in the OTP tree."
+
+ REVISION "9608191700Z"
+ DESCRIPTION
+ "The initial revision of MIB module OTP-OS-MON-MIB."
+ ::= { otpModules 4 }
+
+OTPCounterBasedGauge64 ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The CounterBasedGauge64 type represents a non-negative
+ integer, which may increase or decrease, but shall never
+ exceed a maximum value, nor fall below a minimum value. The
+ maximum value can not be greater than 2^64-1
+ (18446744073709551615 decimal), and the minimum value can
+
+ not be smaller than 0. The value of a CounterBasedGauge64
+ has its maximum value whenever the information being modeled
+ is greater than or equal to its maximum value, and has its
+ minimum value whenever the information being modeled is
+ smaller than or equal to its minimum value. If the
+ information being modeled subsequently decreases below
+ (increases above) the maximum (minimum) value, the
+ CounterBasedGauge64 also decreases (increases).
+
+ Note that this TC is not strictly supported in SMIv2,
+ because the 'always increasing' and 'counter wrap' semantics
+ associated with the Counter64 base type are not preserved.
+ It is possible that management applications which rely
+ solely upon the (Counter64) ASN.1 tag to determine object
+ semantics will mistakenly operate upon objects of this type
+ as they would for Counter64 objects.
+
+ This textual convention represents a limited and short-term
+ solution, and may be deprecated as a long term solution is
+ defined and deployed to replace it."
+ SYNTAX Counter64
+
+otpOsMonMIB OBJECT IDENTIFIER ::= { otpApplications 2 }
+otpOsMonMIBConformance
+ OBJECT IDENTIFIER ::= { otpOsMonMIB 1 }
+otpOsMonMIBObjects
+ OBJECT IDENTIFIER ::= { otpOsMonMIB 2 }
+otpOsMonMIBAlarms
+ OBJECT IDENTIFIER ::= { otpOsMonMIB 4 }
+otpOsMonMIBAlarmsV2
+ OBJECT IDENTIFIER ::= { otpOsMonMIBAlarms 0 }
+
+
+-- Datatypes
+
+-- Managed Objects
+
+load OBJECT IDENTIFIER ::= { otpOsMonMIBObjects 1 }
+disk OBJECT IDENTIFIER ::= { otpOsMonMIBObjects 2 }
+
+loadMemorySystemWatermark OBJECT-TYPE
+ SYNTAX Integer32 (0..100)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Threshold in percent of the total available system
+ memory, which specifies how much memory can be allocated
+ by the system before an alarm is sent."
+ ::= { load 1 }
+
+loadMemoryErlProcWatermark OBJECT-TYPE
+ SYNTAX Integer32 (0..100)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Threshold in percent of the total available system
+ memory, which specifies how much memory can be allocated
+ by one Erlang process before an alarm is sent."
+ ::= { load 2 }
+
+loadTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF LoadEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table with load and memory information
+ for each node."
+ ::= { load 3 }
+
+loadEntry OBJECT-TYPE
+ SYNTAX LoadEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A conceptual row in the loadTable."
+ INDEX { loadErlNodeName }
+ ::= { loadTable 1 }
+
+LoadEntry ::= SEQUENCE {
+ loadErlNodeName DisplayString,
+ loadSystemTotalMemory Gauge32,
+ loadSystemUsedMemory Gauge32,
+ loadLargestErlProcess DisplayString,
+ loadLargestErlProcessUsedMemory Gauge32,
+ loadCpuLoad Integer32,
+ loadCpuLoad5 Integer32,
+ loadCpuLoad15 Integer32,
+ loadOsWordsize Unsigned32,
+ loadSystemTotalMemory64 OTPCounterBasedGauge64,
+ loadSystemUsedMemory64 OTPCounterBasedGauge64,
+ loadLargestErlProcessUsedMemory64 OTPCounterBasedGauge64
+ }
+
+loadErlNodeName OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The name of the erlang node, e.g. erlnode@host1."
+ ::= { loadEntry 1 }
+
+loadSystemTotalMemory OBJECT-TYPE
+ SYNTAX Gauge32
+ UNITS "bytes"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The amount of total memory in the system."
+ ::= { loadEntry 2 }
+
+loadSystemUsedMemory OBJECT-TYPE
+ SYNTAX Gauge32
+ UNITS "bytes"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The amount of used memory."
+ ::= { loadEntry 3 }
+
+loadLargestErlProcess OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The process identifier (Pid) of the largest Erlang
+ process."
+ ::= { loadEntry 4 }
+
+loadLargestErlProcessUsedMemory OBJECT-TYPE
+ SYNTAX Gauge32
+ UNITS "bytes"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The amount of memory used by the largest Erlang
+ process."
+ ::= { loadEntry 5 }
+
+loadCpuLoad OBJECT-TYPE
+ SYNTAX Integer32 (0..100)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The average load the last minute in percent of the CPU
+ where the Erlang node runs."
+ ::= { loadEntry 6 }
+
+loadCpuLoad5 OBJECT-TYPE
+ SYNTAX Integer32 (0..100)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The average load the last 5 minutes in percent of the CPU
+ where the Erlang node runs."
+ ::= { loadEntry 7}
+
+loadCpuLoad15 OBJECT-TYPE
+ SYNTAX Integer32 (0..100)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The average load the last 15 minutes in percent of the CPU
+ where the Erlang node runs."
+ ::= { loadEntry 8}
+
+loadOsWordsize OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The wordsize of the operating operating system."
+ ::= { loadEntry 9 }
+
+loadSystemTotalMemory64 OBJECT-TYPE
+ SYNTAX OTPCounterBasedGauge64
+ UNITS "bytes"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The amount of total memory in the system for 64-bit operating system."
+ ::= { loadEntry 10 }
+
+loadSystemUsedMemory64 OBJECT-TYPE
+ SYNTAX OTPCounterBasedGauge64
+ UNITS "bytes"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The amount of used memory for 64-bit operating system."
+ ::= { loadEntry 11 }
+
+loadLargestErlProcessUsedMemory64 OBJECT-TYPE
+ SYNTAX OTPCounterBasedGauge64
+ UNITS "bytes"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The amount of memory used by the largest Erlang
+ process for 64-bit operating system.."
+ ::= { loadEntry 12 }
+
+diskAlmostFullThreshold OBJECT-TYPE
+ SYNTAX Integer32 (0..100)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Threshold in percent of the available disk space,
+ which specifies how much disk space can be used by
+ a disk or partition before an alarm is sent."
+ ::= { disk 1 }
+
+diskTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF DiskEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table with all local disks or partitions on each
+ node."
+ ::= { disk 2 }
+
+diskEntry OBJECT-TYPE
+ SYNTAX DiskEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A conceptual row in the diskTable."
+ INDEX { erlNodeId, diskId }
+ ::= { diskTable 1 }
+
+DiskEntry ::= SEQUENCE {
+ diskId Integer32,
+ diskDescr DisplayString,
+ diskKBytes Gauge32,
+ diskCapacity Integer32
+ }
+
+diskId OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An integer that uniquely identifies the disk
+ or partition."
+ ::= { diskEntry 1 }
+
+diskDescr OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "A string that identifies the disk or partition."
+ ::= { diskEntry 2 }
+
+diskKBytes OBJECT-TYPE
+ SYNTAX Gauge32
+ UNITS "kbytes"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The amount of total disk/partition space. "
+ ::= { diskEntry 3 }
+
+diskCapacity OBJECT-TYPE
+ SYNTAX Integer32 (0..100)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "How much of the disk's/partition's total capacity has
+ been used, in percent."
+ ::= { diskEntry 4 }
+
+
+-- conformance information
+
+otpOsMonMIBCompliances
+ OBJECT IDENTIFIER ::= { otpOsMonMIBConformance 1 }
+otpOsMonMIBGroups
+ OBJECT IDENTIFIER ::= { otpOsMonMIBConformance 2 }
+
+
+-- compliance statements
+
+otpOsMonBasicCompliance MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "The compliance statement for SNMPv2 entities which
+ implement the OTP-OS-MON-MIB."
+ MODULE -- this module
+ GROUP loadGroup
+ DESCRIPTION
+ "This group is mandatory for systems implementing the
+ load supervison functionality."
+ GROUP loadAlarmsGroup
+ DESCRIPTION
+ "This group is optional for systems implementing the
+ load supervison functionality."
+ GROUP diskGroup
+ DESCRIPTION
+ "This group is mandatory for system implementing the
+ disk supervison functionality."
+ GROUP diskAlarmsGroup
+ DESCRIPTION
+ "This group is optional for systems implementing the
+ disk supervison functionality."
+ ::= { otpOsMonMIBCompliances 1 }
+
+
+-- units of conformance
+
+loadGroup OBJECT-GROUP
+ OBJECTS { loadMemorySystemWatermark,
+ loadMemoryErlProcWatermark,
+ loadSystemTotalMemory,
+ loadSystemUsedMemory,
+ loadLargestErlProcess,
+ loadLargestErlProcessUsedMemory,
+ loadCpuLoad,
+ loadCpuLoad5,
+ loadCpuLoad15,
+ loadOsWordsize,
+ loadSystemTotalMemory64,
+ loadSystemUsedMemory64,
+ loadLargestErlProcessUsedMemory64}
+ STATUS current
+ DESCRIPTION
+ "A collection of objects providing basic instrumentation
+ of the load of the OTP system."
+ ::= { otpOsMonMIBGroups 1 }
+
+diskGroup OBJECT-GROUP
+ OBJECTS { diskAlmostFullThreshold,
+ diskDescr,
+ diskKBytes,
+ diskCapacity }
+ STATUS current
+ DESCRIPTION
+ "A collection of objects providing basic instrumentation
+ of the disks in the OTP system."
+ ::= { otpOsMonMIBGroups 3 }
+
+END
diff --git a/lib/os_mon/mibs/v1/.gitignore b/lib/os_mon/mibs/v1/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/os_mon/mibs/v1/.gitignore
diff --git a/lib/os_mon/priv/bin/.gitignore b/lib/os_mon/priv/bin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/os_mon/priv/bin/.gitignore
diff --git a/lib/os_mon/priv/mibs/.gitignore b/lib/os_mon/priv/mibs/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/os_mon/priv/mibs/.gitignore
diff --git a/lib/os_mon/priv/obj/.gitignore b/lib/os_mon/priv/obj/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/os_mon/priv/obj/.gitignore
diff --git a/lib/os_mon/src/Makefile b/lib/os_mon/src/Makefile
new file mode 100644
index 0000000000..9a75446a89
--- /dev/null
+++ b/lib/os_mon/src/Makefile
@@ -0,0 +1,110 @@
+#
+# %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 $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(OS_MON_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/os_mon-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+MODULES= disksup memsup cpu_sup os_mon os_mon_mib os_sup os_mon_sysinfo \
+ nteventlog
+
+INCLUDE=../include
+CSRC=../c_src
+
+MEMSUP_HRL=$(INCLUDE)/memsup.hrl
+
+HRL_FILES= $(MEMSUP_HRL)
+ERL_FILES= $(MODULES:%=%.erl)
+
+APP_FILE= os_mon.app
+APP_SRC= $(APP_FILE).src
+APP_TARGET=$(EBIN)/$(APP_FILE)
+
+APPUP_FILE= os_mon.appup
+
+APPUP_SRC= $(APPUP_FILE).src
+APPUP_TARGET= ../ebin/$(APPUP_FILE)
+
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS += +warn_obsolete_guard -I$(INCLUDE)
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core *~
+
+docs:
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+#-------------------------------------------------------
+# Special dependencies
+#-------------------------------------------------------
+$(EBIN)/memsup.$(EMULATOR): $(MEMSUP_HRL)
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+
+release_docs_spec:
+
+
+
+
+
+
+
diff --git a/lib/os_mon/src/cpu_sup.erl b/lib/os_mon/src/cpu_sup.erl
new file mode 100644
index 0000000000..742e20b1fa
--- /dev/null
+++ b/lib/os_mon/src/cpu_sup.erl
@@ -0,0 +1,776 @@
+%%
+%% %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%
+%%
+-module(cpu_sup).
+
+%% API
+-export([start_link/0, start/0, stop/0]).
+-export([nprocs/0, avg1/0, avg5/0, avg15/0, util/0, util/1]).
+-export([dummy_reply/1]).
+
+%% For testing
+-export([ping/0]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+%% Internal protocol with the port program
+-define(nprocs,"n").
+-define(avg1,"1").
+-define(avg5,"5").
+-define(avg15,"f").
+-define(quit,"q").
+-define(ping,"p").
+-define(util,"u").
+
+-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(INT32(D3,D2,D1,D0),
+ (((D3) bsl 24) bor ((D2) bsl 16) bor ((D1) bsl 8) bor (D0))).
+
+-define(MAX_UINT32, ((1 bsl 32) - 1)).
+
+-record(cpu_util, {cpu, busy = [], non_busy = []}).
+
+-record(state, {server, os_type}).
+%-record(state, {server, port = not_used, util = [], os_type}).
+
+-record(internal, {port = not_used, util = [], os_type}).
+
+%%----------------------------------------------------------------------
+%% Contract specifications
+%%----------------------------------------------------------------------
+
+-type(util_cpus() :: 'all' | integer() | [integer()]).
+-type(util_state() ::
+ 'user' |
+ 'nice_user' |
+ 'kernel' |
+ 'wait' |
+ 'idle').
+-type(util_value() :: {util_state(), float()} | float()).
+-type(util_desc() :: {util_cpus(), util_value(), util_value(), []}).
+
+%%----------------------------------------------------------------------
+%% Exported functions
+%%----------------------------------------------------------------------
+
+start() ->
+ gen_server:start({local, cpu_sup}, cpu_sup, [], []).
+
+start_link() ->
+ gen_server:start_link({local, cpu_sup}, cpu_sup, [], []).
+
+stop() ->
+ gen_server:call(cpu_sup, ?quit, infinity).
+
+-spec(nprocs/0 :: () -> integer() | {'error', any()}).
+
+nprocs() ->
+ os_mon:call(cpu_sup, ?nprocs, infinity).
+
+-spec(avg1/0 :: () -> integer() | {'error', any()}).
+
+avg1() ->
+ os_mon:call(cpu_sup, ?avg1, infinity).
+
+-spec(avg5/0 :: () -> integer() | {'error', any()}).
+
+avg5() ->
+ os_mon:call(cpu_sup, ?avg5, infinity).
+
+-spec(avg15/0 :: () -> integer() | {'error', any()}).
+
+avg15() ->
+ os_mon:call(cpu_sup, ?avg15, infinity).
+
+-spec(util/1 :: ([ 'detailed' | 'per_cpu']) ->
+ util_desc() | [util_desc()] | {'error', any()}).
+
+util(Args) when is_list (Args) ->
+ % Get arguments
+ case lists:foldl(
+ fun (detailed, {_ , PC}) -> {true, PC };
+ (per_cpu , {D , _ }) -> {D , true};
+ (_ , _ ) -> badarg
+ end, {false, false}, Args) of
+ badarg ->
+ erlang:error(badarg);
+ {Detailed, PerCpu} ->
+ os_mon:call(cpu_sup, {?util, Detailed, PerCpu}, infinity)
+ end;
+util(_) ->
+ erlang:error(badarg).
+
+-spec(util/0 :: () -> float()).
+
+util() ->
+ case util([]) of
+ {all, Busy, _, _} -> Busy;
+ Error -> Error
+ end.
+
+dummy_reply(?nprocs) -> 0;
+dummy_reply(?avg1) -> 0;
+dummy_reply(?avg5) -> 0;
+dummy_reply(?avg15) -> 0;
+dummy_reply({?util,_,_}) -> {all, 0, 0, []}.
+
+%%----------------------------------------------------------------------
+%% For testing
+%%----------------------------------------------------------------------
+
+ping() ->
+ gen_server:call(cpu_sup,?ping).
+
+%%----------------------------------------------------------------------
+%% gen_server callbacks
+%%----------------------------------------------------------------------
+
+%% init
+init([]) ->
+ process_flag(trap_exit, true),
+ process_flag(priority, low),
+ {ok,
+ #state{ os_type = os:type(),
+ server = measurement_server_start()
+ }
+ }.
+handle_call(?quit, _From, State) ->
+ {stop, normal, ok, State};
+handle_call({?util, D, PC}, {Client, _Tag},
+ #state{os_type = {unix, Flavor}} = State)
+ when Flavor == sunos;
+ Flavor == linux ->
+ case measurement_server_call(State#state.server, {?util, D, PC, Client}) of
+ {error, Reason} ->
+ { reply,
+ {error, Reason},
+ State#state{server=measurement_server_restart(State#state.server)}
+ };
+ Result -> {reply, Result, State}
+ end;
+handle_call({?util, Detailed, PerCpu}, _From, State) ->
+ String = "OS_MON (cpu_sup), util/1 unavailable for this OS~n",
+ error_logger:warning_msg(String),
+ {reply, dummy_reply({?util, Detailed, PerCpu}), State};
+handle_call(Request, _From, State) when Request==?nprocs;
+ Request==?avg1;
+ Request==?avg5;
+ Request==?avg15;
+ Request==?ping ->
+ case measurement_server_call(State#state.server, Request) of
+ {error, Reason} ->
+ { reply,
+ {error, Reason},
+ State#state{server=measurement_server_restart(State#state.server)}
+ };
+ Result -> {reply, Result, State}
+ end.
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+handle_info({'EXIT', _Port, Reason}, State) ->
+ {stop, {server_died, Reason}, State#state{server=not_used}};
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, State) ->
+ exit(State#state.server, normal).
+
+%% os_mon-2.0
+%% For live downgrade to/upgrade from os_mon-1.8[.1]
+code_change(Vsn, PrevState, "1.8") ->
+ case Vsn of
+
+ %% Downgrade from this version
+ {down, _Vsn} ->
+ process_flag(trap_exit, false);
+
+ %% Upgrade to this version
+ _Vsn ->
+ process_flag(trap_exit, true)
+ end,
+ {ok, PrevState};
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%----------------------------------------------------------------------
+%% internal functions
+%%----------------------------------------------------------------------
+
+get_uint32_measurement(Request, #internal{port = P, os_type = {unix, sunos}}) ->
+ port_server_call(P, Request);
+get_uint32_measurement(Request, #internal{os_type = {unix, linux}}) ->
+ {ok,F} = file:open("/proc/loadavg",[read,raw]),
+ {ok,D} = file:read(F,24),
+ ok = file:close(F),
+ {ok,[Load1,Load5,Load15,_PRun,PTotal],_} = io_lib:fread("~f ~f ~f ~d/~d", D),
+ case Request of
+ ?avg1 -> sunify(Load1);
+ ?avg5 -> sunify(Load5);
+ ?avg15 -> sunify(Load15);
+ ?ping -> 4711;
+ ?nprocs -> PTotal
+ end;
+get_uint32_measurement(Request, #internal{os_type = {unix, freebsd}}) ->
+ D = os:cmd("/sbin/sysctl -n vm.loadavg") -- "\n",
+ {ok,[Load1,Load5,Load15],_} = io_lib:fread("{ ~f ~f ~f }", D),
+ %% We could count the lines from the ps command as well
+ case Request of
+ ?avg1 -> sunify(Load1);
+ ?avg5 -> sunify(Load5);
+ ?avg15 -> sunify(Load15);
+ ?ping -> 4711;
+ ?nprocs ->
+ Ps = os:cmd("/bin/ps -ax | /usr/bin/wc -l"),
+ {ok, [N], _} = io_lib:fread("~d", Ps),
+ N-1
+ end;
+get_uint32_measurement(Request, #internal{os_type = {unix, dragonfly}}) ->
+ D = os:cmd("/sbin/sysctl -n vm.loadavg") -- "\n",
+ {ok,[Load1,Load5,Load15],_} = io_lib:fread("{ ~f ~f ~f }", D),
+ %% We could count the lines from the ps command as well
+ case Request of
+ ?avg1 -> sunify(Load1);
+ ?avg5 -> sunify(Load5);
+ ?avg15 -> sunify(Load15);
+ ?ping -> 4711;
+ ?nprocs ->
+ Ps = os:cmd("/bin/ps -ax | /usr/bin/wc -l"),
+ {ok, [N], _} = io_lib:fread("~d", Ps),
+ N-1
+ end;
+get_uint32_measurement(Request, #internal{os_type = {unix, openbsd}}) ->
+ D = os:cmd("/sbin/sysctl -n vm.loadavg") -- "\n",
+ {ok, [L1, L5, L15], _} = io_lib:fread("~f ~f ~f", D),
+ case Request of
+ ?avg1 -> sunify(L1);
+ ?avg5 -> sunify(L5);
+ ?avg15 -> sunify(L15);
+ ?ping -> 4711;
+ ?nprocs ->
+ Ps = os:cmd("/bin/ps -ax | /usr/bin/wc -l"),
+ {ok, [N], _} = io_lib:fread("~d", Ps),
+ N-1
+ end;
+get_uint32_measurement(Request, #internal{os_type = {unix, darwin}}) ->
+ %% Get the load average using uptime, overriding Locale setting.
+ D = os:cmd("LANG=C LC_ALL=C uptime") -- "\n",
+ %% Here is a sample uptime string from Mac OS 10.3.8 (C Locale):
+ %% "11:17 up 12 days, 20:39, 2 users, load averages: 1.07 0.95 0.66"
+ %% The safest way to extract the load averages seems to be grab everything
+ %% after the last colon and then do an fread on that.
+ Avg = lists:reverse(hd(string:tokens(lists:reverse(D), ":"))),
+ {ok,[L1,L5,L15],_} = io_lib:fread("~f ~f ~f", Avg),
+
+ case Request of
+ ?avg1 -> sunify(L1);
+ ?avg5 -> sunify(L5);
+ ?avg15 -> sunify(L15);
+ ?ping -> 4711;
+ ?nprocs ->
+ Ps = os:cmd("/bin/ps -ax | /usr/bin/wc -l"),
+ {ok, [N], _} = io_lib:fread("~d", Ps),
+ N-1
+ end;
+get_uint32_measurement(Request, #internal{os_type = {unix, Sys}}) when Sys == irix64;
+ Sys == irix ->
+ %% Get the load average using uptime.
+ %% "8:01pm up 2 days, 22:12, 4 users, load average: 0.70, 0.58, 0.43"
+ D = os:cmd("uptime") -- "\n",
+ Avg = lists:reverse(hd(string:tokens(lists:reverse(D), ":"))),
+ {ok, [L1, L5, L15], _} = io_lib:fread("~f, ~f, ~f", Avg),
+ case Request of
+ ?avg1 -> sunify(L1);
+ ?avg5 -> sunify(L5);
+ ?avg15 -> sunify(L15);
+ ?ping -> 4711;
+ ?nprocs ->
+ {ok, ProcList} = file:list_dir("/proc/pinfo"),
+ length(ProcList)
+ end;
+get_uint32_measurement(_, _) ->
+ throw(not_implemented).
+
+
+get_util_measurement(?util, #internal{port = P }) ->
+ case port_server_call(P, ?util) of
+ {error, Error} -> {error, Error};
+ NewCpuUtil -> NewCpuUtil
+ end;
+get_util_measurement(_,_) ->
+ throw(not_implemented).
+
+%%----------------------------------------------------------------------
+%% BEGIN: tainted internal functions
+%%----------------------------------------------------------------------
+
+sunify(Val) ->
+ round(Val*256). % Note that Solaris and Linux load averages are
+ % measured quite differently anyway
+
+
+keysearchdelete(_, _, []) ->
+ {false, []};
+keysearchdelete(K, N, [T|Ts]) when element(N, T) == K ->
+ {{value, T}, Ts};
+keysearchdelete(K, N, [T|Ts]) ->
+ {X, NTs} = keysearchdelete(K, N, Ts),
+ {X, [T|NTs]}.
+
+%% Internal cpu utilization functions
+
+%% cpu_util_diff(New, Old) takes a list of new cpu_util records as first
+%% argument and a list of old cpu_util records as second argument. The
+%% two lists have to be sorted on cpu index in ascending order.
+%%
+%% The returned value is a difference list in descending order.
+cpu_util_diff(New, Old) ->
+ cpu_util_diff(New, Old, []).
+
+cpu_util_diff([], [], Acc) ->
+ Acc;
+cpu_util_diff([#cpu_util{cpu = Cpu,
+ busy = NewBusy,
+ non_busy = NewNonBusy} | NewCpuUtils],
+ [#cpu_util{cpu = Cpu,
+ busy = OldBusy,
+ non_busy = OldNonBusy} | OldCpuUtils],
+ Acc) ->
+ {PreBusy, GotBusy} = state_list_diff(NewBusy, OldBusy),
+ {NonBusy, GotNonBusy} = state_list_diff(NewNonBusy, OldNonBusy),
+ Busy = case GotBusy orelse GotNonBusy of
+ true ->
+ PreBusy;
+ false ->
+ %% This can happen if cpu_sup:util/[0,1] is called
+ %% again immediately after the previous call has
+ %% returned. Because the user obviously is doing
+ %% something we charge "user".
+ lists:map(fun ({user, 0}) -> {user, 1};
+ ({_, 0} = StateTup) -> StateTup
+ end,
+ PreBusy)
+ end,
+cpu_util_diff(NewCpuUtils, OldCpuUtils, [#cpu_util{cpu = Cpu,
+ busy = Busy,
+ non_busy = NonBusy}
+ | Acc]);
+
+%% A new cpu appeared
+cpu_util_diff([#cpu_util{cpu = NC}|_] = New,
+ [#cpu_util{cpu = OC}|_] = Old,
+ Acc) when NC < OC ->
+cpu_util_diff(New, [#cpu_util{cpu = NC}|Old], Acc);
+cpu_util_diff([#cpu_util{cpu = NC}|_] = New, [], Acc) ->
+cpu_util_diff(New, [#cpu_util{cpu = NC}], Acc);
+
+%% An old cpu disappeared
+cpu_util_diff([#cpu_util{cpu = NC}|Ns],
+ [#cpu_util{cpu = OC}|_] = Old,
+ Acc) when NC > OC ->
+cpu_util_diff(Ns, Old, Acc);
+cpu_util_diff([], _Old, Acc) ->
+cpu_util_diff([], [], Acc).
+
+cpu_util_rel(NewCpuUtils, OldCpuUtils, Detailed, PerCpu) ->
+ cpu_util_rel(cpu_util_diff(NewCpuUtils, OldCpuUtils), Detailed, PerCpu).
+
+%%
+%% cpu_util_rel/3 takes a difference list of cpu_util records as first
+%% argument, a boolean determining if the result should be detailed as
+%% second argument, and a boolean determining if the result should be
+%% per cpu as third argument. The first argument (the difference list)
+%% has to be sorted on cpu index in descending order.
+%%
+cpu_util_rel(CUDiff, false, false) ->
+ {B, T} = lists:foldl(fun (#cpu_util{busy = BusyList,
+ non_busy = NonBusyList},
+ {BusyAcc, TotAcc}) ->
+ Busy = state_list_sum(BusyList),
+ NonBusy = state_list_sum(NonBusyList),
+ {BusyAcc+Busy, TotAcc+Busy+NonBusy}
+ end,
+ {0, 0},
+ CUDiff),
+ BRel = B/T*100,
+ {all, BRel, 100-BRel, []};
+cpu_util_rel(CUDiff, true, false) ->
+ cpu_util_rel_det(CUDiff, #cpu_util{cpu = [], busy = [], non_busy = []});
+cpu_util_rel(CUDiff, false, true) ->
+ cpu_util_rel_pcpu(CUDiff, []);
+cpu_util_rel(CUDiff, true, true) ->
+ cpu_util_rel_det_pcpu(CUDiff, []).
+
+cpu_util_rel_pcpu([], Acc) ->
+ Acc;
+cpu_util_rel_pcpu([#cpu_util{cpu = C,
+ busy = BusyList,
+ non_busy = NonBusyList} | Rest], Acc) ->
+ Busy = state_list_sum(BusyList),
+ NonBusy = state_list_sum(NonBusyList),
+ Tot = Busy + NonBusy,
+ cpu_util_rel_pcpu(Rest, [{C, Busy/Tot*100, NonBusy/Tot*100, []}|Acc]).
+
+cpu_util_rel_det([], #cpu_util{cpu = CpuAcc,
+ busy = BusyAcc,
+ non_busy = NonBusyAcc}) ->
+ Total = state_list_sum(BusyAcc) + state_list_sum(NonBusyAcc),
+ {CpuAcc, mk_rel_states(BusyAcc,Total), mk_rel_states(NonBusyAcc,Total), []};
+cpu_util_rel_det([#cpu_util{cpu = Cpu,
+ busy = Busy,
+ non_busy = NonBusy} | Rest],
+ #cpu_util{cpu = CpuAcc,
+ busy = BusyAcc,
+ non_busy = NonBusyAcc}) ->
+ cpu_util_rel_det(Rest, #cpu_util{cpu = [Cpu|CpuAcc],
+ busy = state_list_add(Busy,
+ BusyAcc),
+ non_busy = state_list_add(NonBusy,
+ NonBusyAcc)}).
+
+cpu_util_rel_det_pcpu([], Acc) ->
+ Acc;
+cpu_util_rel_det_pcpu([#cpu_util{cpu = Cpu,
+ busy = Busy,
+ non_busy = NonBusy}| Rest], Acc) ->
+ Total = state_list_sum(Busy) + state_list_sum(NonBusy),
+ cpu_util_rel_det_pcpu(Rest,
+ [{Cpu,
+ mk_rel_states(Busy, Total),
+ mk_rel_states(NonBusy, Total),
+ []} | Acc]).
+
+mk_rel_states(States, Total) ->
+ lists:map(fun ({State, Value}) -> {State, 100*Value/Total} end, States).
+
+state_list_sum(StateList) ->
+ lists:foldl(fun ({_, X}, Acc) -> Acc+X end, 0, StateList).
+
+state_list_diff([],[]) ->
+ {[], false};
+state_list_diff([{State,ValueNew}|RestNew], []) ->
+ state_list_diff([{State, ValueNew} | RestNew], [{State, 0}]);
+state_list_diff([{State,ValueNew}|RestNew], [{State,ValueOld}|RestOld]) ->
+ ValDiff = val_diff(State, ValueNew, ValueOld),
+ {RestStateDiff, FoundDiff} = state_list_diff(RestNew, RestOld),
+ {[{State, ValDiff} | RestStateDiff], FoundDiff orelse ValDiff /= 0}.
+
+state_list_add([],[]) ->
+ [];
+state_list_add([{State, ValueA}|RestA], []) ->
+ [{State, ValueA} | state_list_add(RestA, [])];
+state_list_add([{State, ValueA} | RestA], [{State, ValueB} | RestB]) ->
+ [{State, ValueA + ValueB} | state_list_add(RestA, RestB)].
+
+one_step_backwards(State, New, Old) ->
+ case os:type() of
+ {unix, linux} ->
+ %% This should never happen! But values sometimes takes a step
+ %% backwards on linux. We'll ignore it as long as it's only
+ %% one step...
+ 0;
+ _ ->
+ val_diff2(State, New, Old)
+ end.
+
+val_diff(State, New, Old) when New == Old - 1 ->
+ one_step_backwards(State, New, Old);
+val_diff(State, ?MAX_UINT32, 0) ->
+ one_step_backwards(State, ?MAX_UINT32, 0);
+val_diff(State, New, Old) ->
+ val_diff2(State, New, Old).
+
+val_diff2(State, New, Old) when New > ?MAX_UINT32; Old > ?MAX_UINT32 ->
+ %% We obviously got uints > 32 bits
+ ensure_positive_diff(State, New - Old);
+val_diff2(State, New, Old) when New < Old ->
+ %% 32-bit integer wrapped
+ ensure_positive_diff(State, (?MAX_UINT32 + 1) + New - Old);
+val_diff2(_State, New, Old) ->
+ New - Old.
+
+ensure_positive_diff(_State, Diff) when Diff >= 0 ->
+ Diff;
+ensure_positive_diff(State, Diff) ->
+ throw({error, {negative_diff, State, Diff}}).
+%%----------------------------------------------------------------------
+%% END: tainted internal functions
+%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+%% cpu_sup measurement server wrapper
+%%----------------------------------------------------------------------
+
+measurement_server_call(Pid, Request) ->
+ Timeout = 5000,
+ Pid ! {self(), Request},
+ receive
+ {data, Data} -> Data
+ after Timeout ->
+ {error, timeout}
+ end.
+
+measurement_server_restart(Pid) ->
+ exit(Pid, kill),
+ measurement_server_start().
+
+measurement_server_start() ->
+ spawn(fun() -> measurement_server_init() end).
+
+measurement_server_init() ->
+ process_flag(trap_exit, true),
+ OS = os:type(),
+ Server = case OS of
+ {unix, Flavor} when Flavor==sunos;
+ Flavor==linux ->
+ port_server_start();
+ {unix, Flavor} when Flavor==darwin;
+ Flavor==freebsd;
+ Flavor==dragonfly;
+ Flavor==openbsd;
+ Flavor==irix64;
+ Flavor==irix ->
+ not_used;
+ _ ->
+ exit({unsupported_os, OS})
+ end,
+ measurement_server_loop(#internal{port=Server, os_type=OS}).
+
+measurement_server_loop(State) ->
+ receive
+ {_, quit} ->
+ State#internal.port ! {self(), ?quit},
+ ok;
+ {'DOWN',Monitor,process,_,_} ->
+ measurement_server_loop(State#internal{ util = lists:keydelete(
+ Monitor,
+ 2,
+ State#internal.util)});
+ {Pid, {?util, D, PC, Client}} ->
+ {Monitor, OldCpuUtil, Utils2} = case keysearchdelete(Client, 1, State#internal.util) of
+ {{value, {Client, Mon, U}}, Us} -> {Mon, U, Us};
+ {false, Us} -> {erlang:monitor(process, Client), [], Us}
+ end,
+ try get_util_measurement(?util, State) of
+ NewCpuUtil ->
+ Result = cpu_util_rel(NewCpuUtil, OldCpuUtil, D, PC),
+ Pid ! {data, Result},
+ measurement_server_loop(State#internal{util=[{Client,Monitor,NewCpuUtil}|Utils2]})
+ catch
+ Error ->
+ Pid ! {error, Error},
+ measurement_server_loop(State)
+ end;
+ {Pid, Request} ->
+ try get_uint32_measurement(Request, State) of
+ Result -> Pid ! {data, Result}
+ catch
+ Error -> Pid ! {error, Error}
+ end,
+ measurement_server_loop(State);
+ {'EXIT', Pid, _n} when State#internal.port == Pid ->
+ measurement_server_loop(State#internal{port = port_server_start()});
+ _Other ->
+ measurement_server_loop(State)
+ end.
+
+%%----------------------------------------------------------------------
+%% cpu_sup port program server wrapper
+%%----------------------------------------------------------------------
+
+port_server_call(Pid, Command) ->
+ Pid ! {self(), Command},
+ receive
+ {Pid, {data, Result}} -> Result;
+ {Pid, {error, Reason}} -> {error, Reason}
+ end.
+
+port_server_start() ->
+ Timeout = 6000,
+ Pid = spawn_link(fun() -> port_server_init(Timeout) end),
+ Pid ! {self(), ?ping},
+ receive
+ {Pid, {data,4711}} -> Pid;
+ {error,Reason} -> {error, Reason}
+ after Timeout ->
+ {error, timeout}
+ end.
+
+port_server_init(Timeout) ->
+ Port = start_portprogram(),
+ port_server_loop(Port, Timeout).
+
+port_server_loop(Port, Timeout) ->
+ receive
+
+ % Adjust timeout
+ {Pid, {timeout, Timeout}} ->
+ Pid ! {data, Timeout},
+ port_server_loop(Port, Timeout);
+ % Number of processors
+ {Pid, ?nprocs} ->
+ port_command(Port, ?nprocs),
+ Result = port_receive_uint32(Port, Timeout),
+ Pid ! {self(), {data, Result}},
+ port_server_loop(Port, Timeout);
+
+ % Average load for the past minute
+ {Pid, ?avg1} ->
+ port_command(Port, ?avg1),
+ Result = port_receive_uint32(Port, Timeout),
+ Pid ! {self(), {data, Result}},
+ port_server_loop(Port, Timeout);
+
+ % Average load for the past five minutes
+ {Pid, ?avg5} ->
+ port_command(Port, ?avg5),
+ Result = port_receive_uint32(Port, Timeout),
+ Pid ! {self(), {data, Result}},
+ port_server_loop(Port, Timeout);
+
+ % Average load for the past 15 minutes
+ {Pid, ?avg15} ->
+ port_command(Port, ?avg15),
+ Result = port_receive_uint32(Port, Timeout),
+ Pid ! {self(), {data, Result}},
+ port_server_loop(Port, Timeout);
+
+ {Pid, ?util} ->
+ port_command(Port, ?util),
+ Result = port_receive_util(Port, Timeout),
+ Pid ! {self(), {data, Result}},
+ port_server_loop(Port, Timeout);
+
+ % Port ping
+ {Pid, ?ping} ->
+ port_command(Port, ?ping),
+ Result = port_receive_uint32(Port, Timeout),
+ Pid ! {self(), {data, Result}},
+ port_server_loop(Port, Timeout);
+
+ % Close port and this server
+ {Pid, ?quit} ->
+ port_command(Port, ?quit),
+ port_close(Port),
+ Pid ! {self(), {data, quit}},
+ ok;
+
+ % Ignore other commands
+ _ -> port_server_loop(Port, Timeout)
+ end.
+
+port_receive_uint32( Port, Timeout) -> port_receive_uint32(Port, Timeout, []).
+port_receive_uint32(_Port, _Timeout, [D3,D2,D1,D0]) -> ?INT32(D3,D2,D1,D0);
+port_receive_uint32(_Port, _Timeout, [_,_,_,_ | G]) -> exit({port_garbage, G});
+port_receive_uint32(Port, Timeout, D) ->
+ receive
+ {'EXIT', Port, Reason} -> exit({port_exit, Reason});
+ {Port, {data, ND}} -> port_receive_uint32(Port, Timeout, D ++ ND)
+ after Timeout -> exit(timeout_uint32) end.
+
+port_receive_util(Port, Timeout) ->
+ receive
+ {Port, {data, [ NP3,NP2,NP1,NP0, % Number of processors
+ NE3,NE2,NE1,NE0 % Number of entries per processor
+ | CpuData]}} ->
+ port_receive_cpu_util( ?INT32(NP3,NP2,NP1,NP0),
+ ?INT32(NE3,NE2,NE1,NE0),
+ CpuData, []);
+ {'EXIT', Port, Reason} -> exit({port_exit, Reason})
+ after Timeout -> exit(timeout_util) end.
+
+% per processor receive loop
+port_receive_cpu_util(0, _NE, [], CpuList) ->
+ % Return in ascending cpu_id order
+ lists:reverse(CpuList);
+port_receive_cpu_util(0, _NE, Garbage, _) ->
+ exit( {port_garbage, Garbage});
+port_receive_cpu_util(NP, NE, CpuData, CpuList) ->
+ {CpuUtil, Rest} = port_receive_cpu_util_entries(NE, #cpu_util{}, CpuData),
+ port_receive_cpu_util(NP - 1, NE, Rest, [ CpuUtil | CpuList]).
+
+% per entry receive loop
+port_receive_cpu_util_entries(0, CU, Rest) ->
+ {CU, Rest};
+port_receive_cpu_util_entries(NE, CU,
+ [ CID3, CID2, CID1, CID0,
+ Val3, Val2, Val1, Val0 |
+ CpuData]) ->
+
+ TagId = ?INT32(CID3,CID2,CID1,CID0),
+ Value = ?INT32(Val3,Val2,Val1,Val0),
+
+ % Conversions from integers to atoms
+ case TagId of
+ ?cu_cpu_id ->
+ NewCU = CU#cpu_util{cpu = Value},
+ port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
+ ?cu_user ->
+ NewCU = CU#cpu_util{
+ busy = [{user, Value} | CU#cpu_util.busy] },
+ port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
+ ?cu_nice_user ->
+ NewCU = CU#cpu_util{
+ busy = [{nice_user, Value} | CU#cpu_util.busy] },
+ port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
+ ?cu_kernel ->
+ NewCU = CU#cpu_util{
+ busy = [{kernel, Value} | CU#cpu_util.busy] },
+ port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
+ ?cu_io_wait ->
+ NewCU = CU#cpu_util{
+ non_busy = [{wait, Value} | CU#cpu_util.non_busy] },
+ port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
+ ?cu_idle ->
+ NewCU = CU#cpu_util{
+ non_busy = [{idle, Value} | CU#cpu_util.non_busy] },
+ port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
+ ?cu_hard_irq ->
+ NewCU = CU#cpu_util{
+ busy = [{hard_irq, Value} | CU#cpu_util.busy] },
+ port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
+ ?cu_soft_irq ->
+ NewCU = CU#cpu_util{
+ busy = [{soft_irq, Value} | CU#cpu_util.busy] },
+ port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
+ ?cu_steal ->
+ NewCU = CU#cpu_util{
+ non_busy = [{steal, Value} | CU#cpu_util.non_busy] },
+ port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
+ Unhandled ->
+ exit({unexpected_type_id, Unhandled})
+ end;
+port_receive_cpu_util_entries(_, _, Data) ->
+ exit({data_mismatch, Data}).
+
+start_portprogram() ->
+ Command = filename:join([code:priv_dir(os_mon), "bin", "cpu_sup"]),
+ Port = open_port({spawn, Command}, [stream]),
+ port_command(Port, ?ping),
+ 4711 = port_receive_uint32(Port, 5000),
+ Port.
diff --git a/lib/os_mon/src/disksup.erl b/lib/os_mon/src/disksup.erl
new file mode 100644
index 0000000000..3340f7ee72
--- /dev/null
+++ b/lib/os_mon/src/disksup.erl
@@ -0,0 +1,369 @@
+%%
+%% %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%
+%%
+-module(disksup).
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0]).
+-export([get_disk_data/0,
+ get_check_interval/0, set_check_interval/1,
+ get_almost_full_threshold/0, set_almost_full_threshold/1]).
+-export([dummy_reply/1, param_type/2, param_default/1]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+%% Other exports
+-export([format_status/2]).
+
+-record(state, {threshold, timeout, os, diskdata = [],port}).
+
+%%----------------------------------------------------------------------
+%% API
+%%----------------------------------------------------------------------
+
+start_link() ->
+ gen_server:start_link({local, disksup}, disksup, [], []).
+
+get_disk_data() ->
+ os_mon:call(disksup, get_disk_data).
+
+get_check_interval() ->
+ os_mon:call(disksup, get_check_interval).
+set_check_interval(Minutes) ->
+ case param_type(disk_space_check_interval, Minutes) of
+ true ->
+ os_mon:call(disksup, {set_check_interval, Minutes});
+ false ->
+ erlang:error(badarg)
+ end.
+
+get_almost_full_threshold() ->
+ os_mon:call(disksup, get_almost_full_threshold).
+set_almost_full_threshold(Float) ->
+ case param_type(disk_almost_full_threshold, Float) of
+ true ->
+ os_mon:call(disksup, {set_almost_full_threshold, Float});
+ false ->
+ erlang:error(badarg)
+ end.
+
+dummy_reply(get_disk_data) ->
+ [{"none", 0, 0}];
+dummy_reply(get_check_interval) ->
+ minutes_to_ms(os_mon:get_env(disksup, disk_space_check_interval));
+dummy_reply({set_check_interval, _}) ->
+ ok;
+dummy_reply(get_almost_full_threshold) ->
+ round(os_mon:get_env(disksup, disk_almost_full_threshold) * 100);
+dummy_reply({set_almost_full_threshold, _}) ->
+ ok.
+
+param_type(disk_space_check_interval, Val) when is_integer(Val),
+ Val>=1 -> true;
+param_type(disk_almost_full_threshold, Val) when is_number(Val),
+ 0=<Val,
+ Val=<1 -> true;
+param_type(_Param, _Val) -> false.
+
+param_default(disk_space_check_interval) -> 30;
+param_default(disk_almost_full_threshold) -> 0.80.
+
+%%----------------------------------------------------------------------
+%% gen_server callbacks
+%%----------------------------------------------------------------------
+
+init([]) ->
+ process_flag(trap_exit, true),
+ process_flag(priority, low),
+
+ OS = get_os(),
+ Port = case OS of
+ {unix, Flavor} when Flavor==sunos4;
+ Flavor==solaris;
+ Flavor==freebsd;
+ Flavor==dragonfly;
+ Flavor==darwin;
+ Flavor==linux;
+ Flavor==openbsd;
+ Flavor==irix64;
+ Flavor==irix ->
+ start_portprogram();
+ {win32, _OSname} ->
+ not_used;
+ _ ->
+ exit({unsupported_os, OS})
+ end,
+
+ %% Read the values of some configuration parameters
+ Threshold = os_mon:get_env(disksup, disk_almost_full_threshold),
+ Timeout = os_mon:get_env(disksup, disk_space_check_interval),
+
+ %% Initiation first disk check
+ self() ! timeout,
+
+ {ok, #state{port=Port, os=OS,
+ threshold=round(Threshold*100),
+ timeout=minutes_to_ms(Timeout)}}.
+
+handle_call(get_disk_data, _From, State) ->
+ {reply, State#state.diskdata, State};
+
+handle_call(get_check_interval, _From, State) ->
+ {reply, State#state.timeout, State};
+handle_call({set_check_interval, Minutes}, _From, State) ->
+ Timeout = minutes_to_ms(Minutes),
+ {reply, ok, State#state{timeout=Timeout}};
+
+handle_call(get_almost_full_threshold, _From, State) ->
+ {reply, State#state.threshold, State};
+handle_call({set_almost_full_threshold, Float}, _From, State) ->
+ Threshold = round(Float * 100),
+ {reply, ok, State#state{threshold=Threshold}};
+
+handle_call({set_threshold, Threshold}, _From, State) -> % test only
+ {reply, ok, State#state{threshold=Threshold}}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info(timeout, State) ->
+ NewDiskData = check_disk_space(State#state.os, State#state.port,
+ State#state.threshold),
+ timer:send_after(State#state.timeout, timeout),
+ {noreply, State#state{diskdata = NewDiskData}};
+handle_info({'EXIT', _Port, Reason}, State) ->
+ {stop, {port_died, Reason}, State#state{port=not_used}};
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, State) ->
+ clear_alarms(),
+ case State#state.port of
+ not_used ->
+ ok;
+ Port ->
+ port_close(Port)
+ end,
+ ok.
+
+%% os_mon-2.0.1
+%% For live downgrade to/upgrade from os_mon-1.8[.1]
+code_change(Vsn, PrevState, "1.8") ->
+ case Vsn of
+
+ %% Downgrade from this version
+ {down, _Vsn} ->
+ State = case PrevState#state.port of
+ not_used -> PrevState#state{port=noport};
+ _ -> PrevState
+ end,
+ {ok, State};
+
+ %% Upgrade to this version
+ _Vsn ->
+ State = case PrevState#state.port of
+ noport -> PrevState#state{port=not_used};
+ _ -> PrevState
+ end,
+ {ok, State}
+ end;
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%----------------------------------------------------------------------
+%% Other exports
+%%----------------------------------------------------------------------
+
+format_status(_Opt, [_PDict, #state{os = OS, threshold = Threshold,
+ timeout = Timeout,
+ diskdata = DiskData}]) ->
+ [{data, [{"OS", OS},
+ {"Timeout", Timeout},
+ {"Threshold", Threshold},
+ {"DiskData", DiskData}]}].
+
+%%----------------------------------------------------------------------
+%% Internal functions
+%%----------------------------------------------------------------------
+
+get_os() ->
+ case os:type() of
+ {unix, sunos} ->
+ case os:version() of
+ {5,_,_} -> {unix, solaris};
+ {4,_,_} -> {unix, sunos4};
+ V -> exit({unknown_os_version, V})
+ end;
+ {unix, irix64} -> {unix, irix};
+ OS ->
+ OS
+ end.
+
+%%--Port handling functions---------------------------------------------
+
+start_portprogram() ->
+ open_port({spawn, "sh -s disksup 2>&1"}, [stream]).
+
+my_cmd(Cmd0, Port) ->
+ %% Insert a new line after the command, in case the command
+ %% contains a comment character
+ Cmd = io_lib:format("(~s\n) </dev/null; echo \"\^M\"\n", [Cmd0]),
+ Port ! {self(), {command, [Cmd, 10]}},
+ get_reply(Port, []).
+
+get_reply(Port, O) ->
+ receive
+ {Port, {data, N}} ->
+ case newline(N, O) of
+ {ok, Str} -> Str;
+ {more, Acc} -> get_reply(Port, Acc)
+ end;
+ {'EXIT', Port, Reason} ->
+ exit({port_died, Reason})
+ end.
+
+newline([13|_], B) -> {ok, lists:reverse(B)};
+newline([H|T], B) -> newline(T, [H|B]);
+newline([], B) -> {more, B}.
+
+%%--Check disk space----------------------------------------------------
+
+check_disk_space({win32,_}, not_used, Threshold) ->
+ Result = os_mon_sysinfo:get_disk_info(),
+ check_disks_win32(Result, Threshold);
+check_disk_space({unix, solaris}, Port, Threshold) ->
+ Result = my_cmd("/usr/bin/df -lk", Port),
+ check_disks_solaris(skip_to_eol(Result), Threshold);
+check_disk_space({unix, irix}, Port, Threshold) ->
+ Result = my_cmd("/usr/sbin/df -lk",Port),
+ check_disks_irix(skip_to_eol(Result), Threshold);
+check_disk_space({unix, linux}, Port, Threshold) ->
+ Result = my_cmd("/bin/df -lk", Port),
+ check_disks_solaris(skip_to_eol(Result), Threshold);
+check_disk_space({unix, dragonfly}, Port, Threshold) ->
+ Result = my_cmd("/bin/df -k -t ufs,hammer", Port),
+ check_disks_solaris(skip_to_eol(Result), Threshold);
+check_disk_space({unix, freebsd}, Port, Threshold) ->
+ Result = my_cmd("/bin/df -k -t ufs", Port),
+ check_disks_solaris(skip_to_eol(Result), Threshold);
+check_disk_space({unix, openbsd}, Port, Threshold) ->
+ Result = my_cmd("/bin/df -k -t ffs", Port),
+ check_disks_solaris(skip_to_eol(Result), Threshold);
+check_disk_space({unix, sunos4}, Port, Threshold) ->
+ Result = my_cmd("df", Port),
+ check_disks_solaris(skip_to_eol(Result), Threshold);
+check_disk_space({unix, darwin}, Port, Threshold) ->
+ Result = my_cmd("/bin/df -k -t ufs,hfs", Port),
+ check_disks_solaris(skip_to_eol(Result), Threshold).
+
+% This code works for Linux and FreeBSD as well
+check_disks_solaris("", _Threshold) ->
+ [];
+check_disks_solaris("\n", _Threshold) ->
+ [];
+check_disks_solaris(Str, Threshold) ->
+ case io_lib:fread("~s~d~d~d~d%~s", Str) of
+ {ok, [_FS, KB, _Used, _Avail, Cap, MntOn], RestStr} ->
+ if
+ Cap >= Threshold ->
+ set_alarm({disk_almost_full, MntOn}, []);
+ true ->
+ clear_alarm({disk_almost_full, MntOn})
+ end,
+ [{MntOn, KB, Cap} |
+ check_disks_solaris(RestStr, Threshold)];
+ _Other ->
+ check_disks_solaris(skip_to_eol(Str),Threshold)
+ end.
+
+%% Irix: like Linux with an extra FS type column and no '%'.
+check_disks_irix("", _Threshold) -> [];
+check_disks_irix("\n", _Threshold) -> [];
+check_disks_irix(Str, Threshold) ->
+ case io_lib:fread("~s~s~d~d~d~d~s", Str) of
+ {ok, [_FS, _FSType, KB, _Used, _Avail, Cap, MntOn], RestStr} ->
+ if Cap >= Threshold -> set_alarm({disk_almost_full, MntOn}, []);
+ true -> clear_alarm({disk_almost_full, MntOn}) end,
+ [{MntOn, KB, Cap} | check_disks_irix(RestStr, Threshold)];
+ _Other ->
+ check_disks_irix(skip_to_eol(Str),Threshold)
+ end.
+
+check_disks_win32([], _Threshold) ->
+ [];
+check_disks_win32([H|T], Threshold) ->
+ case io_lib:fread("~s~s~d~d~d", H) of
+ {ok, [Drive,"DRIVE_FIXED",BAvail,BTot,_TotFree], _RestStr} ->
+ Cap = trunc((BTot-BAvail) / BTot * 100),
+ if
+ Cap >= Threshold ->
+ set_alarm({disk_almost_full, Drive}, []);
+ true ->
+ clear_alarm({disk_almost_full, Drive})
+ end,
+ [{Drive, BTot div 1024, Cap} |
+ check_disks_win32(T, Threshold)]; % Return Total Capacity in Kbytes
+ {ok,_,_RestStr} ->
+ check_disks_win32(T,Threshold);
+ _Other ->
+ []
+ end.
+
+%%--Alarm handling------------------------------------------------------
+
+set_alarm(AlarmId, AlarmDescr) ->
+ case get(AlarmId) of
+ set ->
+ ok;
+ undefined ->
+ alarm_handler:set_alarm({AlarmId, AlarmDescr}),
+ put(AlarmId, set)
+ end.
+
+clear_alarm(AlarmId) ->
+ case get(AlarmId) of
+ set ->
+ alarm_handler:clear_alarm(AlarmId),
+ erase(AlarmId);
+ undefined ->
+ ok
+ end.
+
+clear_alarms() ->
+ lists:foreach(fun({{disk_almost_full, _MntOn} = AlarmId, set}) ->
+ alarm_handler:clear_alarm(AlarmId);
+ (_Other) ->
+ ignore
+ end,
+ get()).
+
+%%--Auxiliary-----------------------------------------------------------
+
+%% Type conversion
+minutes_to_ms(Minutes) ->
+ trunc(60000*Minutes).
+
+skip_to_eol([]) ->
+ [];
+skip_to_eol([$\n | T]) ->
+ T;
+skip_to_eol([_ | T]) ->
+ skip_to_eol(T).
diff --git a/lib/os_mon/src/memsup.erl b/lib/os_mon/src/memsup.erl
new file mode 100644
index 0000000000..822e1f939c
--- /dev/null
+++ b/lib/os_mon/src/memsup.erl
@@ -0,0 +1,1022 @@
+%%
+%% %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%
+%%
+-module(memsup).
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0]). % for supervisor
+-export([get_memory_data/0, get_system_memory_data/0,
+ get_check_interval/0, set_check_interval/1,
+ get_procmem_high_watermark/0, set_procmem_high_watermark/1,
+ get_sysmem_high_watermark/0, set_sysmem_high_watermark/1,
+ get_helper_timeout/0, set_helper_timeout/1,
+ get_os_wordsize/0]).
+-export([dummy_reply/1, param_type/2, param_default/1]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+%% Other exports
+-export([format_status/2]).
+
+-include("memsup.hrl").
+
+-record(state,
+ {os, % {OSfamily,OSname} | OSfamily
+ port_mode, % bool()
+
+ mem_usage, % undefined | {Alloc, Total}
+ worst_mem_user, % undefined | {Pid, Alloc}
+
+ sys_only, % bool() memsup_system_only
+ timeout, % int() memory_check_interval, ms
+ helper_timeout, % int() memsup_helper_timeout, ms
+ sys_mem_watermark, % float() system_memory_high_watermark, %
+ proc_mem_watermark, % float() process_memory_high_watermark, %
+
+ pid, % undefined | pid()
+ wd_timer, % undefined | TimerRef
+ ext_wd_timer, % undefined | TimerRef
+ pending = [], % [reg | {reg,From} | {ext,From}]
+ ext_pending = [] % [{ext,From}]
+ }).
+
+%%----------------------------------------------------------------------
+%% API
+%%----------------------------------------------------------------------
+
+start_link() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+get_os_wordsize() ->
+ os_mon:call(memsup, get_os_wordsize, infinity).
+
+get_memory_data() ->
+ os_mon:call(memsup, get_memory_data, infinity).
+
+get_system_memory_data() ->
+ os_mon:call(memsup, get_system_memory_data, infinity).
+
+get_check_interval() ->
+ os_mon:call(memsup, get_check_interval, infinity).
+set_check_interval(Minutes) ->
+ case param_type(memory_check_interval, Minutes) of
+ true ->
+ MS = minutes_to_ms(Minutes), % for backwards compatibility
+ os_mon:call(memsup, {set_check_interval, MS}, infinity);
+ false ->
+ erlang:error(badarg)
+ end.
+
+get_procmem_high_watermark() ->
+ os_mon:call(memsup, get_procmem_high_watermark, infinity).
+set_procmem_high_watermark(Float) ->
+ case param_type(process_memory_high_watermark, Float) of
+ true ->
+ os_mon:call(memsup, {set_procmem_high_watermark, Float},
+ infinity);
+ false ->
+ erlang:error(badarg)
+ end.
+
+get_sysmem_high_watermark() ->
+ os_mon:call(memsup, get_sysmem_high_watermark, infinity).
+set_sysmem_high_watermark(Float) ->
+ case param_type(system_memory_high_watermark, Float) of
+ true ->
+ os_mon:call(memsup, {set_sysmem_high_watermark, Float},
+ infinity);
+ false ->
+ erlang:error(badarg)
+ end.
+
+get_helper_timeout() ->
+ os_mon:call(memsup, get_helper_timeout, infinity).
+set_helper_timeout(Seconds) ->
+ case param_type(memsup_helper_timeout, Seconds) of
+ true ->
+ os_mon:call(memsup, {set_helper_timeout, Seconds});
+ false ->
+ erlang:error(badarg)
+ end.
+
+dummy_reply(get_memory_data) ->
+ dummy_reply(get_memory_data,
+ os_mon:get_env(memsup, memsup_system_only));
+dummy_reply(get_system_memory_data) ->
+ [];
+dummy_reply(get_os_wordsize) ->
+ 0;
+dummy_reply(get_check_interval) ->
+ minutes_to_ms(os_mon:get_env(memsup, memory_check_interval));
+dummy_reply({set_check_interval, _}) ->
+ ok;
+dummy_reply(get_procmem_high_watermark) ->
+ trunc(100 * os_mon:get_env(memsup, process_memory_high_watermark));
+dummy_reply({set_procmem_high_watermark, _}) ->
+ ok;
+dummy_reply(get_sysmem_high_watermark) ->
+ trunc(100 * os_mon:get_env(memsup, system_memory_high_watermark));
+dummy_reply({set_sysmem_high_watermark, _}) ->
+ ok;
+dummy_reply(get_helper_timeout) ->
+ os_mon:get_env(memsup, memsup_helper_timeout);
+dummy_reply({set_helper_timeout, _}) ->
+ ok.
+dummy_reply(get_memory_data, true) ->
+ {0,0,undefined};
+dummy_reply(get_memory_data, false) ->
+ {0,0,{self(),0}}.
+
+param_type(memsup_system_only, Val) when Val==true; Val==false -> true;
+param_type(memory_check_interval, Val) when is_integer(Val),
+ Val>0 -> true;
+param_type(memsup_helper_timeout, Val) when is_integer(Val),
+ Val>0 -> true;
+param_type(system_memory_high_watermark, Val) when is_number(Val),
+ 0=<Val,
+ Val=<1 -> true;
+param_type(process_memory_high_watermark, Val) when is_number(Val),
+ 0=<Val,
+ Val=<1 -> true;
+param_type(_Param, _Val) -> false.
+
+param_default(memsup_system_only) -> false;
+param_default(memory_check_interval) -> 1;
+param_default(memsup_helper_timeout) -> 30;
+param_default(system_memory_high_watermark) -> 0.80;
+param_default(process_memory_high_watermark) -> 0.05.
+
+%%----------------------------------------------------------------------
+%% gen_server callbacks
+%%----------------------------------------------------------------------
+
+init([]) ->
+ process_flag(trap_exit, true),
+ process_flag(priority, low),
+
+ OS = os:type(),
+ PortMode = case OS of
+ {unix, darwin} -> false;
+ {unix, freebsd} -> false;
+ % Linux supports this.
+ {unix, linux} -> true;
+ {unix, openbsd} -> true;
+ {unix, irix64} -> true;
+ {unix, irix} -> true;
+ {unix, sunos} -> true;
+ {win32, _OSname} -> false;
+ vxworks -> true;
+ _ ->
+ exit({unsupported_os, OS})
+ end,
+ Pid = if
+ PortMode ->
+ spawn_link(fun() -> port_init() end);
+ not PortMode ->
+ undefined
+ end,
+
+ %% Read the values of some configuration parameters
+ SysOnly = os_mon:get_env(memsup, memsup_system_only),
+ Timeout = os_mon:get_env(memsup, memory_check_interval),
+ HelperTimeout = os_mon:get_env(memsup, memsup_helper_timeout),
+ SysMem = os_mon:get_env(memsup, system_memory_high_watermark),
+ ProcMem = os_mon:get_env(memsup, process_memory_high_watermark),
+
+ %% Initiate first data collection
+ self() ! time_to_collect,
+
+ {ok, #state{os=OS, port_mode=PortMode,
+
+ sys_only = SysOnly,
+ timeout = minutes_to_ms(Timeout),
+ helper_timeout = sec_to_ms(HelperTimeout),
+ sys_mem_watermark = SysMem,
+ proc_mem_watermark = ProcMem,
+
+ pid=Pid}}.
+
+handle_call(get_os_wordsize, _From, State) ->
+ Wordsize = get_os_wordsize(State#state.os),
+ {reply, Wordsize, State};
+handle_call(get_memory_data, From, State) ->
+ %% Return result of latest memory check
+ case State#state.mem_usage of
+ {Alloc, Total} ->
+ Worst = State#state.worst_mem_user,
+ {reply, {Total, Alloc, Worst}, State};
+
+ %% Special case: get_memory_data called before any memory data
+ %% has been collected
+ undefined ->
+ case State#state.wd_timer of
+ undefined ->
+ WDTimer = erlang:send_after(State#state.timeout,
+ self(),
+ reg_collection_timeout),
+ Pending = [{reg,From}],
+ if
+ State#state.port_mode ->
+ State#state.pid ! {self(), collect_sys},
+ {noreply, State#state{wd_timer=WDTimer,
+ pending=Pending}};
+ true ->
+ OS = State#state.os,
+ Self = self(),
+ Pid = spawn_link(fun() ->
+ MU = get_memory_usage(OS),
+ Self ! {collected_sys,MU}
+ end),
+ {noreply, State#state{pid=Pid,
+ wd_timer=WDTimer,
+ pending=Pending}}
+ end;
+ _TimerRef ->
+ Pending = [{reg,From} | State#state.pending],
+ {noreply, State#state{pending=Pending}}
+ end
+ end;
+
+handle_call(get_system_memory_data,From,#state{port_mode=true}=State) ->
+ %% When using a port, the extensive memory collection is slightly
+ %% different than a regular one
+ case State#state.ext_wd_timer of
+ undefined ->
+ WDTimer = erlang:send_after(State#state.helper_timeout,
+ self(),
+ ext_collection_timeout),
+ State#state.pid ! {self(), collect_ext_sys},
+ {noreply, State#state{ext_wd_timer=WDTimer,
+ ext_pending=[{ext,From}]}};
+ _TimerRef ->
+ Pending = [{ext,From} | State#state.ext_pending],
+ {noreply, State#state{ext_pending=Pending}}
+ end;
+handle_call(get_system_memory_data, From, State) ->
+ %% When not using a port, the regular memory collection is used
+ %% for extensive memory data as well
+ case State#state.wd_timer of
+ undefined ->
+ WDTimer = erlang:send_after(State#state.helper_timeout,
+ self(),
+ reg_collection_timeout),
+ OS = State#state.os,
+ Self = self(),
+ Pid = spawn_link(fun() ->
+ MemUsage = get_memory_usage(OS),
+ Self ! {collected_sys, MemUsage}
+ end),
+ {noreply, State#state{pid=Pid, wd_timer=WDTimer,
+ pending=[{ext,From}]}};
+ _TimerRef ->
+ Pending = [{ext,From} | State#state.pending],
+ {noreply, State#state{pending=Pending}}
+ end;
+
+handle_call(get_check_interval, _From, State) ->
+ {reply, State#state.timeout, State};
+handle_call({set_check_interval, MS}, _From, State) ->
+ {reply, ok, State#state{timeout=MS}};
+
+handle_call(get_procmem_high_watermark, _From, State) ->
+ {reply, trunc(100 * State#state.proc_mem_watermark), State};
+handle_call({set_procmem_high_watermark, Float}, _From, State) ->
+ {reply, ok, State#state{proc_mem_watermark=Float}};
+
+handle_call(get_sysmem_high_watermark, _From, State) ->
+ {reply, trunc(100 * State#state.sys_mem_watermark), State};
+handle_call({set_sysmem_high_watermark, Float}, _From, State) ->
+ {reply, ok, State#state{sys_mem_watermark=Float}};
+
+handle_call(get_helper_timeout, _From, State) ->
+ {reply, ms_to_sec(State#state.helper_timeout), State};
+handle_call({set_helper_timeout, Seconds}, _From, State) ->
+ {reply, ok, State#state{helper_timeout=sec_to_ms(Seconds)}};
+
+%% The following are only for test purposes (whitebox testing).
+handle_call({set_sys_hw, HW}, _From, State) ->
+ {reply, ok, State#state{sys_mem_watermark=HW}};
+handle_call({set_pid_hw, HW}, _From, State) ->
+ {reply, ok, State#state{proc_mem_watermark=HW}};
+handle_call(get_state, _From, State) ->
+ {reply, State, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+%% It's time to check memory
+handle_info(time_to_collect, State) ->
+ case State#state.wd_timer of
+ undefined ->
+ WDTimer = erlang:send_after(State#state.helper_timeout,
+ self(),
+ reg_collection_timeout),
+ if
+ State#state.port_mode ->
+ State#state.pid ! {self(), collect_sys},
+ {noreply, State#state{wd_timer=WDTimer,
+ pending=[reg]}};
+ true ->
+ OS = State#state.os,
+ Self = self(),
+ Pid = spawn_link(fun() ->
+ MU = get_memory_usage(OS),
+ Self ! {collected_sys,MU}
+ end),
+ {noreply, State#state{pid=Pid, wd_timer=WDTimer,
+ pending=[reg]}}
+ end;
+ _TimerRef ->
+ {noreply, State#state{pending=[reg|State#state.pending]}}
+ end;
+
+%% Memory data collected
+handle_info({collected_sys, {Alloc,Total}}, State) ->
+
+ %% Cancel watchdog timer (and as a security measure,
+ %% also flush any reg_collection_timeout message)
+ TimeSpent = case erlang:cancel_timer(State#state.wd_timer) of
+ false ->
+ State#state.helper_timeout;
+ TimeLeft ->
+ State#state.helper_timeout-TimeLeft
+ end,
+ flush(reg_collection_timeout),
+
+ %% First check if this is the result of a periodic memory check
+ %% and update alarms and State if this is the case
+ State2 =
+ case lists:member(reg, State#state.pending) of
+ true ->
+
+ %% Check if system alarm should be set/cleared
+ if
+ Alloc > State#state.sys_mem_watermark*Total ->
+ set_alarm(system_memory_high_watermark, []);
+ true ->
+ clear_alarm(system_memory_high_watermark)
+ end,
+
+ %% Check if process data should be collected
+ case State#state.sys_only of
+ false ->
+ {Pid, Bytes} = get_worst_memory_user(),
+ Threshold= State#state.proc_mem_watermark*Total,
+
+ %% Check if process alarm should be set/cleared
+ if
+ Bytes > Threshold ->
+ set_alarm(process_memory_high_watermark,
+ Pid);
+ true ->
+ clear_alarm(process_memory_high_watermark)
+ end,
+
+ State#state{mem_usage={Alloc, Total},
+ worst_mem_user={Pid, Bytes}};
+ true ->
+ State#state{mem_usage={Alloc, Total}}
+ end;
+ false ->
+ State
+ end,
+
+ %% Then send a reply to all waiting clients, in preserved time order
+ Worst = State2#state.worst_mem_user,
+ SysMemUsage = get_ext_memory_usage(State2#state.os, {Alloc,Total}),
+ reply(State2#state.pending, {Total,Alloc,Worst}, SysMemUsage),
+
+ %% Last, if this was a periodic check, start a timer for the next
+ %% one. New timeout = interval-time spent collecting,
+ case lists:member(reg, State#state.pending) of
+ true ->
+ Time = case State2#state.timeout - TimeSpent of
+ MS when MS<0 ->
+ 0;
+ MS ->
+ MS
+ end,
+ erlang:send_after(Time, self(), time_to_collect);
+ false ->
+ ignore
+ end,
+ {noreply, State2#state{wd_timer=undefined, pending=[]}};
+handle_info({'EXIT', Pid, normal}, State) when is_pid(Pid) ->
+ %% Temporary pid terminating when job is done
+ {noreply, State};
+
+%% Timeout during data collection
+handle_info(reg_collection_timeout, State) ->
+
+ %% Cancel memory collection (and as a security measure,
+ %% also flush any collected_sys message)
+ if
+ State#state.port_mode -> State#state.pid ! cancel;
+ true -> exit(State#state.pid, cancel)
+ end,
+ flush(collected_sys),
+
+ %% Issue a warning message
+ Str = "OS_MON (memsup) timeout, no data collected~n",
+ error_logger:warning_msg(Str),
+
+ %% Send a dummy reply to all waiting clients, preserving time order
+ reply(State#state.pending,
+ dummy_reply(get_memory_data, State#state.sys_only),
+ dummy_reply(get_system_memory_data)),
+
+ %% If it is a periodic check which has timed out, start a timer for
+ %% the next one
+ %% New timeout = interval-helper timeout
+ case lists:member(reg, State#state.pending) of
+ true ->
+ Time =
+ case State#state.timeout-State#state.helper_timeout of
+ MS when MS<0 -> 0;
+ MS -> MS
+ end,
+ erlang:send_after(Time, self(), time_to_collect);
+ false ->
+ ignore
+ end,
+ {noreply, State#state{wd_timer=undefined, pending=[]}};
+handle_info({'EXIT', Pid, cancel}, State) when is_pid(Pid) ->
+ %% Temporary pid terminating as ordered
+ {noreply, State};
+
+%% Extensive memory data collected (port_mode==true only)
+handle_info({collected_ext_sys, SysMemUsage}, State) ->
+
+ %% Cancel watchdog timer (and as a security mearure,
+ %% also flush any ext_collection_timeout message)
+ erlang:cancel_timer(State#state.ext_wd_timer),
+ flush(ext_collection_timeout),
+
+ %% Send the reply to all waiting clients, preserving time order
+ reply(State#state.ext_pending, undef, SysMemUsage),
+
+ {noreply, State#state{ext_wd_timer=undefined, ext_pending=[]}};
+
+%% Timeout during ext memory data collection (port_mode==true only)
+handle_info(ext_collection_timeout, State) ->
+
+ %% Cancel memory collection (and as a security measure,
+ %% also flush any collected_ext_sys message)
+ State#state.pid ! ext_cancel,
+ flush(collected_ext_sys),
+
+ %% Issue a warning message
+ Str = "OS_MON (memsup) timeout, no data collected~n",
+ error_logger:warning_msg(Str),
+
+ %% Send a dummy reply to all waiting clients, preserving time order
+ SysMemUsage = dummy_reply(get_system_memory_data),
+ reply(State#state.ext_pending, undef, SysMemUsage),
+
+ {noreply, State#state{ext_wd_timer=undefined, ext_pending=[]}};
+
+%% Error in data collecting (port connected or temporary) process
+handle_info({'EXIT', Pid, Reason}, State) when is_pid(Pid) ->
+ {stop, Reason, State};
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, State) ->
+ if
+ State#state.port_mode -> State#state.pid ! close;
+ true -> ok
+ end,
+ clear_alarms(),
+ ok.
+
+%% os_mon-2.0.1
+%% For live downgrade to/upgrade from os_mon-1.8[.1] and -2.0
+code_change(Vsn, PrevState, "1.8") ->
+ case Vsn of
+
+ %% Downgrade from this version
+ {down, _Vsn} ->
+
+ %% Kill the helper process, if there is one,
+ %% and flush messages from it
+ case PrevState#state.pid of
+ Pid when is_pid(Pid) ->
+ unlink(Pid), % to prevent 'EXIT' message
+ exit(Pid, cancel);
+ undefined -> ignore
+ end,
+ flush(collected_sys),
+ flush(collected_ext_sys),
+
+ %% Cancel timers, flush timeout messages
+ %% and send dummy replies to any pending clients
+ case PrevState#state.wd_timer of
+ undefined ->
+ ignore;
+ TimerRef1 ->
+ erlang:cancel_timer(TimerRef1),
+ SysOnly = PrevState#state.sys_only,
+ MemUsage = dummy_reply(get_memory_data, SysOnly),
+ SysMemUsage1 = dummy_reply(get_system_memory_data),
+ reply(PrevState#state.pending,MemUsage,SysMemUsage1)
+ end,
+ case PrevState#state.ext_wd_timer of
+ undefined ->
+ ignore;
+ TimerRef2 ->
+ erlang:cancel_timer(TimerRef2),
+ SysMemUsage2 = dummy_reply(get_system_memory_data),
+ reply(PrevState#state.pending, undef, SysMemUsage2)
+ end,
+ flush(reg_collection_timeout),
+ flush(ext_collection_timeout),
+
+ %% Downgrade to old state record
+ State = {state,
+ PrevState#state.timeout,
+ PrevState#state.mem_usage,
+ PrevState#state.worst_mem_user,
+ PrevState#state.sys_mem_watermark,
+ PrevState#state.proc_mem_watermark,
+ not PrevState#state.sys_only, % collect_procmem
+ undefined, % wd_timer
+ [], % pending
+ undefined, % ext_wd_timer
+ [], % ext_pending
+ PrevState#state.helper_timeout},
+ {ok, State};
+
+ %% Upgrade to this version
+ _Vsn ->
+
+ %% Old state record
+ {state,
+ Timeout, MemUsage, WorstMemUser,
+ SysMemWatermark, ProcMemWatermark, CollProc,
+ WDTimer, Pending, ExtWDTimer, ExtPending,
+ HelperTimeout} = PrevState,
+ SysOnly = not CollProc,
+
+ %% Flush memsup_helper messages
+ flush(collected_sys),
+ flush(collected_proc),
+ flush(collected_ext_sys),
+
+ %% Cancel timers, flush timeout messages
+ %% and send dummy replies to any pending clients
+ case WDTimer of
+ undefined ->
+ ignore;
+ TimerRef1 ->
+ erlang:cancel_timer(TimerRef1),
+ MemUsage = dummy_reply(get_memory_data, SysOnly),
+ Pending2 = lists:map(fun(From) -> {reg,From} end,
+ Pending),
+ reply(Pending2, MemUsage, undef)
+ end,
+ case ExtWDTimer of
+ undefined ->
+ ignore;
+ TimerRef2 ->
+ erlang:cancel_timer(TimerRef2),
+ SysMemUsage = dummy_reply(get_system_memory_data),
+ ExtPending2 = lists:map(fun(From) -> {ext,From} end,
+ ExtPending),
+ reply(ExtPending2, undef, SysMemUsage)
+ end,
+ flush(reg_collection_timeout),
+ flush(ext_collection_timeout),
+
+ OS = os:type(),
+ PortMode = case OS of
+ {unix, darwin} -> false;
+ {unix, freebsd} -> false;
+ {unix, linux} -> false;
+ {unix, openbsd} -> true;
+ {unix, sunos} -> true;
+ {win32, _OSname} -> false;
+ vxworks -> true
+ end,
+ Pid = if
+ PortMode -> spawn_link(fun() -> port_init() end);
+ not PortMode -> undefined
+ end,
+
+ %% Upgrade to this state record
+ State = #state{os = OS,
+ port_mode = PortMode,
+ mem_usage = MemUsage,
+ worst_mem_user = WorstMemUser,
+ sys_only = SysOnly,
+ timeout = Timeout,
+ helper_timeout = HelperTimeout,
+ sys_mem_watermark = SysMemWatermark,
+ proc_mem_watermark = ProcMemWatermark,
+ pid = Pid,
+ wd_timer = undefined,
+ ext_wd_timer = undefined,
+ pending = [],
+ ext_pending = []},
+ {ok, State}
+ end;
+code_change(_Vsn, State, "2.0") ->
+
+ %% Restart the port process (it must use new memsup code)
+ Pid = case State#state.port_mode of
+ true ->
+ State#state.pid ! close,
+ spawn_link(fun() -> port_init() end);
+ false ->
+ State#state.pid
+ end,
+ {ok, State#state{pid=Pid}};
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%----------------------------------------------------------------------
+%% Other exports
+%%----------------------------------------------------------------------
+
+format_status(_Opt, [_PDict, #state{timeout=Timeout, mem_usage=MemUsage,
+ worst_mem_user=WorstMemUser}]) ->
+ {Allocated, Total} = MemUsage,
+ WorstMemFormat = case WorstMemUser of
+ {Pid, Mem} ->
+ [{"Pid", Pid}, {"Memory", Mem}];
+ undefined ->
+ undefined
+ end,
+ [{data, [{"Timeout", Timeout}]},
+ {items, {"Memory Usage", [{"Allocated", Allocated},
+ {"Total", Total}]}},
+ {items, {"Worst Memory User", WorstMemFormat}}].
+
+
+%%----------------------------------------------------------------------
+%% Internal functions
+%%----------------------------------------------------------------------
+
+%%-- Fetching kernel bit support ---------------------------------------
+
+get_os_wordsize({unix, sunos}) ->
+ String = clean_string(os:cmd("isainfo -b")),
+ erlang:list_to_integer(String);
+get_os_wordsize({unix, irix64}) -> 64;
+get_os_wordsize({unix, irix}) -> 32;
+get_os_wordsize({unix, linux}) -> get_os_wordsize_with_uname();
+get_os_wordsize({unix, darwin}) -> get_os_wordsize_with_uname();
+get_os_wordsize({unix, netbsd}) -> get_os_wordsize_with_uname();
+get_os_wordsize({unix, freebsd}) -> get_os_wordsize_with_uname();
+get_os_wordsize({unix, openbsd}) -> get_os_wordsize_with_uname();
+get_os_wordsize(_) -> unsupported_os.
+
+get_os_wordsize_with_uname() ->
+ String = clean_string(os:cmd("uname -m")),
+ case String of
+ "x86_64" -> 64;
+ "sparc64" -> 64;
+ _ -> 32
+ end.
+
+clean_string(String) -> lists:flatten(string:tokens(String,"\r\n\t ")).
+
+
+%%--Replying to pending clients-----------------------------------------
+
+reply(Pending, MemUsage, SysMemUsage) ->
+ lists:foreach(fun(reg) ->
+ ignore;
+ ({reg, From}) ->
+ gen_server:reply(From, MemUsage);
+ ({ext, From}) ->
+ gen_server:reply(From, SysMemUsage)
+ end,
+ lists:reverse(Pending)).
+
+%%--Collect memory data, no port----------------------------------------
+
+%% get_memory_usage(OS) -> {Alloc, Total}
+
+%% Darwin:
+%% Uses vm_stat command. This appears to lie about the page size in
+%% Mac OS X 10.2.2 - the pages given are based on 4000 bytes, but
+%% the vm_stat command tells us that it is 4096...
+get_memory_usage({unix,darwin}) ->
+ Str1 = os:cmd("/usr/bin/vm_stat"),
+
+ {[Free], Str2} = fread_value("Pages free:~d.", Str1),
+ {[Active], Str3} = fread_value("Pages active:~d.", Str2),
+ {[Inactive], Str4} = fread_value("Pages inactive:~d.", Str3),
+ {[_], Str5} = fread_value("Pages speculative:~d.", Str4),
+ {[Wired], _} = fread_value("Pages wired down:~d.", Str5),
+
+ NMemUsed = (Wired + Active + Inactive) * 4000,
+ NMemTotal = NMemUsed + Free * 4000,
+ {NMemUsed,NMemTotal};
+
+%% FreeBSD: Look in /usr/include/sys/vmmeter.h for the format of struct
+%% vmmeter
+get_memory_usage({unix,freebsd}) ->
+ PageSize = freebsd_sysctl("vm.stats.vm.v_page_size"),
+ PageCount = freebsd_sysctl("vm.stats.vm.v_page_count"),
+ FreeCount = freebsd_sysctl("vm.stats.vm.v_free_count"),
+ NMemUsed = (PageCount - FreeCount) * PageSize,
+ NMemTotal = PageCount * PageSize,
+ {NMemUsed, NMemTotal};
+
+%% Win32: Find out how much memory is in use by asking
+%% the os_mon_sysinfo process.
+get_memory_usage({win32,_OSname}) ->
+ [Result|_] = os_mon_sysinfo:get_mem_info(),
+ {ok, [_MemLoad, TotPhys, AvailPhys,
+ _TotPage, _AvailPage, _TotV, _AvailV], _RestStr} =
+ io_lib:fread("~d~d~d~d~d~d~d", Result),
+ {TotPhys-AvailPhys, TotPhys}.
+
+fread_value(Format, Str0) ->
+ case io_lib:fread(Format, skip_to_eol(Str0)) of
+ {error, {fread, input}} -> {[0], Str0};
+ {ok, Value, Str1} -> {Value, Str1}
+ end.
+
+skip_to_eol([]) -> [];
+skip_to_eol([$\n | T]) -> T;
+skip_to_eol([_ | T]) -> skip_to_eol(T).
+
+freebsd_sysctl(Def) ->
+ list_to_integer(os:cmd("/sbin/sysctl -n " ++ Def) -- "\n").
+
+%% get_ext_memory_usage(OS, {Alloc, Total}) -> [{Tag, Bytes}]
+get_ext_memory_usage(OS, {Alloc, Total}) ->
+ case OS of
+ {win32, _} ->
+ [{total_memory, Total}, {free_memory, Total-Alloc},
+ {system_total_memory, Total}];
+ {unix, linux} ->
+ [{total_memory, Total}, {free_memory, Total-Alloc},
+ %% corr. unless setrlimit() set
+ {system_total_memory, Total}];
+ {unix, freebsd} ->
+ [{total_memory, Total}, {free_memory, Total-Alloc},
+ {system_total_memory, Total}];
+ {unix, darwin} ->
+ [{total_memory, Total}, {free_memory, Total-Alloc},
+ {system_total_memory, Total}];
+ _ -> % OSs using a port
+ dummy % not sent anyway
+ end.
+
+%%--Collect memory data, using port-------------------------------------
+
+port_init() ->
+ process_flag(trap_exit, true),
+ Port = start_portprogram(),
+ port_idle(Port).
+
+start_portprogram() ->
+ Command = filename:join([code:priv_dir(os_mon), "bin", "memsup"]),
+ open_port({spawn, Command}, [{packet, 1}]).
+
+%% The connected process loops are a bit awkward (several different
+%% functions doing almost the same thing) as
+%% a) strategies for receiving regular memory data and extensive
+%% memory data are different
+%% b) memory collection can be cancelled, in which case the process
+%% should still wait for port response (which should come
+%% eventually!) but not receive any requests or cancellations
+%% meanwhile to prevent getting out of synch.
+port_idle(Port) ->
+ receive
+ {Memsup, collect_sys} ->
+ Port ! {self(), {command, [?SHOW_MEM]}},
+ get_memory_usage(Port, undefined, Memsup);
+ {Memsup, collect_ext_sys} ->
+ Port ! {self(), {command, [?SHOW_SYSTEM_MEM]}},
+ get_ext_memory_usage(Port, [], Memsup);
+ cancel ->
+ %% Received after reply already has been delivered...
+ port_idle(Port);
+ ext_cancel ->
+ %% Received after reply already has been delivered...
+ port_idle(Port);
+ close ->
+ port_close(Port);
+ {Port, {data, Data}} ->
+ exit({port_error, Data});
+ {'EXIT', Port, Reason} ->
+ exit({port_died, Reason});
+ {'EXIT', _Memsup, _Reason} ->
+ port_close(Port)
+ end.
+
+get_memory_usage(Port, Alloc, Memsup) ->
+ receive
+ {Port, {data, Data}} when Alloc==undefined ->
+ get_memory_usage(Port, erlang:list_to_integer(Data, 16), Memsup);
+ {Port, {data, Data}} ->
+ Total = erlang:list_to_integer(Data, 16),
+ Memsup ! {collected_sys, {Alloc, Total}},
+ port_idle(Port);
+ cancel ->
+ get_memory_usage_cancelled(Port, Alloc);
+ close ->
+ port_close(Port);
+ {'EXIT', Port, Reason} ->
+ exit({port_died, Reason});
+ {'EXIT', _Memsup, _Reason} ->
+ port_close(Port)
+ end.
+get_memory_usage_cancelled(Port, Alloc) ->
+ receive
+ {Port, {data, _Data}} when Alloc==undefined ->
+ get_memory_usage_cancelled(Port, 0);
+ {Port, {data, _Data}} ->
+ port_idle(Port);
+ close ->
+ port_close(Port);
+ {'EXIT', Port, Reason} ->
+ exit({port_died, Reason});
+ {'EXIT', _Memsup, _Reason} ->
+ port_close(Port)
+ end.
+
+get_ext_memory_usage(Port, Accum, Memsup) ->
+ Tab = [
+ {?MEM_SYSTEM_TOTAL, system_total_memory},
+ {?MEM_TOTAL, total_memory},
+ {?MEM_FREE, free_memory},
+ {?MEM_BUFFERS, buffered_memory},
+ {?MEM_CACHED, cached_memory},
+ {?MEM_SHARED, shared_memory},
+ {?MEM_LARGEST_FREE, largest_free},
+ {?MEM_NUMBER_OF_FREE, number_of_free},
+ {?SWAP_TOTAL, total_swap},
+ {?SWAP_FREE, free_swap}
+ ],
+ receive
+ {Port, {data, [?SHOW_SYSTEM_MEM_END]}} ->
+ Memsup ! {collected_ext_sys, Accum},
+ port_idle(Port);
+ {Port, {data, [Tag]}} ->
+ case lists:keysearch(Tag, 1, Tab) of
+ {value, {Tag, ATag}} ->
+ get_ext_memory_usage(ATag, Port, Accum, Memsup);
+ _ ->
+ exit({memsup_port_error, {Port,[Tag]}})
+ end;
+ ext_cancel ->
+ get_ext_memory_usage_cancelled(Port);
+ close ->
+ port_close(Port);
+ {'EXIT', Port, Reason} ->
+ exit({port_died, Reason});
+ {'EXIT', _Memsup, _Reason} ->
+ port_close(Port)
+ end.
+get_ext_memory_usage_cancelled(Port) ->
+ Tab = [
+ {?MEM_SYSTEM_TOTAL, system_total_memory},
+ {?MEM_TOTAL, total_memory},
+ {?MEM_FREE, free_memory},
+ {?MEM_BUFFERS, buffered_memory},
+ {?MEM_CACHED, cached_memory},
+ {?MEM_SHARED, shared_memory},
+ {?MEM_LARGEST_FREE, largest_free},
+ {?MEM_NUMBER_OF_FREE, number_of_free},
+ {?SWAP_TOTAL, total_swap},
+ {?SWAP_FREE, free_swap}
+ ],
+ receive
+ {Port, {data, [?SHOW_SYSTEM_MEM_END]}} ->
+ port_idle(Port);
+ {Port, {data, [Tag]}} ->
+ case lists:keysearch(Tag, 1, Tab) of
+ {value, {Tag, ATag}} ->
+ get_ext_memory_usage_cancelled(ATag, Port);
+ _ ->
+ exit({memsup_port_error, {Port,[Tag]}})
+ end;
+ close ->
+ port_close(Port);
+ {'EXIT', Port, Reason} ->
+ exit({port_died, Reason});
+ {'EXIT', _Memsup, _Reason} ->
+ port_close(Port)
+ end.
+
+get_ext_memory_usage(ATag, Port, Accum0, Memsup) ->
+ receive
+ {Port, {data, Data}} ->
+ Accum = [{ATag,erlang:list_to_integer(Data, 16)}|Accum0],
+ get_ext_memory_usage(Port, Accum, Memsup);
+ cancel ->
+ get_ext_memory_usage_cancelled(ATag, Port);
+ close ->
+ port_close(Port);
+ {'EXIT', Port, Reason} ->
+ exit({port_died, Reason});
+ {'EXIT', _Memsup, _Reason} ->
+ port_close(Port)
+ end.
+get_ext_memory_usage_cancelled(_ATag, Port) ->
+ receive
+ {Port, {data, _Data}} ->
+ get_ext_memory_usage_cancelled(Port);
+ close ->
+ port_close(Port);
+ {'EXIT', Port, Reason} ->
+ exit({port_died, Reason});
+ {'EXIT', _Memsup, _Reason} ->
+ port_close(Port)
+ end.
+
+%%--Collect process data------------------------------------------------
+
+%% get_worst_memory_user() -> {Pid, Bytes}
+get_worst_memory_user() ->
+ get_worst_memory_user(processes(), self(), 0).
+
+get_worst_memory_user([Pid|Pids], MaxPid, MaxMemBytes) ->
+ case process_memory(Pid) of
+ undefined ->
+ get_worst_memory_user(Pids, MaxPid, MaxMemBytes);
+ MemoryBytes when MemoryBytes>MaxMemBytes ->
+ get_worst_memory_user(Pids, Pid, MemoryBytes);
+ _MemoryBytes ->
+ get_worst_memory_user(Pids, MaxPid, MaxMemBytes)
+ end;
+get_worst_memory_user([], MaxPid, MaxMemBytes) ->
+ {MaxPid, MaxMemBytes}.
+
+process_memory(Pid) ->
+ case process_info(Pid, memory) of
+ {memory, Bytes} ->
+ Bytes;
+ undefined -> % Pid must have died
+ undefined
+ end.
+
+%%--Alarm handling------------------------------------------------------
+
+set_alarm(AlarmId, AlarmDescr) ->
+ case get(AlarmId) of
+ set ->
+ ok;
+ undefined ->
+ alarm_handler:set_alarm({AlarmId, AlarmDescr}),
+ put(AlarmId, set)
+ end.
+
+clear_alarm(AlarmId) ->
+ case get(AlarmId) of
+ set ->
+ alarm_handler:clear_alarm(AlarmId),
+ erase(AlarmId);
+ _ ->
+ ok
+ end.
+
+clear_alarms() ->
+ lists:foreach(fun({system_memory_high_watermark = Id, set}) ->
+ alarm_handler:clear_alarm(Id);
+ ({process_memory_high_watermark = Id, set}) ->
+ alarm_handler:clear_alarm(Id);
+ (_Other) ->
+ ignore
+ end,
+ get()).
+
+%%--Auxiliary-----------------------------------------------------------
+
+%% Type conversions
+minutes_to_ms(Minutes) -> trunc(60000*Minutes).
+sec_to_ms(Sec) -> trunc(1000*Sec).
+ms_to_sec(MS) -> MS div 1000.
+
+flush(Msg) ->
+ receive
+ {Msg, _} -> true;
+ Msg -> true
+ after 0 ->
+ true
+ end.
diff --git a/lib/os_mon/src/nteventlog.erl b/lib/os_mon/src/nteventlog.erl
new file mode 100644
index 0000000000..d624048c29
--- /dev/null
+++ b/lib/os_mon/src/nteventlog.erl
@@ -0,0 +1,162 @@
+%%
+%% %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(nteventlog).
+-behaviour(gen_server).
+
+%% API
+-export([start_link/2, start/2, stop/0]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-record(state, {port, mfa}).
+
+%%----------------------------------------------------------------------
+%% API
+%%----------------------------------------------------------------------
+
+start_link(Ident, MFA) ->
+ gen_server:start_link({local, nteventlog}, nteventlog,
+ [Ident, MFA], []).
+
+start(Ident, MFA) ->
+ gen_server:start({local, nteventlog}, nteventlog, [Ident, MFA], []).
+
+stop() ->
+ gen_server:call(nteventlog, stop).
+
+%%----------------------------------------------------------------------
+%% gen_server callbacks
+%%----------------------------------------------------------------------
+
+init([Identifier,MFA0]) ->
+ process_flag(trap_exit, true),
+ process_flag(priority, low),
+
+ Port = case os:type() of
+ {win32, _OSname} -> start_portprogram(Identifier);
+ OS -> exit({unsupported_os, OS})
+ end,
+
+ %% If we're using os_sup:error_report/2,
+ %% the setting of os_sup_errortag should be used as argument
+ MFA = case MFA0 of
+ {os_sup, error_report, [_Tag]} ->
+ Tag = os_mon:get_env(os_sup, os_sup_errortag),
+ {os_sup, error_report, [Tag]};
+ _ ->
+ MFA0
+ end,
+
+ {ok, #state{port=Port, mfa=MFA}}.
+
+handle_call(stop, _From, State) ->
+ {stop, normal, stopped, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info({_Port, {data, Data}}, #state{mfa={M,F,A}} = State) ->
+ T = parse_log(Data),
+ apply(M, F, [T | A]),
+ State#state.port ! {self(), {command, "A"}},
+ {noreply, State};
+handle_info({'EXIT', _Port, Reason}, State) ->
+ {stop, {port_died, Reason}, State#state{port=not_used}};
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, State) ->
+ case State#state.port of
+ not_used -> ignore;
+ Port ->
+ port_close(Port)
+ end,
+ ok.
+
+%% os_mon-2.0
+%% For live downgrade to/upgrade from os_mon-1.8[.1]
+code_change(Vsn, PrevState, "1.8") ->
+ case Vsn of
+
+ %% Downgrade from this version
+ {down, _Vsn} ->
+ process_flag(trap_exit, false),
+
+ %% Downgrade to old State tuple
+ State = {PrevState#state.port, PrevState#state.mfa},
+ {ok, State};
+
+ %% Upgrade to this version
+ _Vsn ->
+ process_flag(trap_exit, true),
+
+ %% Upgrade to this state record
+ {Port, MFA} = PrevState,
+ State = #state{port=Port, mfa=MFA},
+ {ok, State}
+ end;
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%----------------------------------------------------------------------
+%% Internal functions
+%%----------------------------------------------------------------------
+
+start_portprogram(Identifier) ->
+ Command =
+ filename:join([code:priv_dir(os_mon),"bin","nteventlog.exe"]) ++
+ " " ++ make_list(Identifier),
+ open_port({spawn,Command},[{packet,2}]).
+
+make_list(X) when is_atom(X) ->
+ atom_to_list(X);
+make_list(X) ->
+ X.
+
+holl_len([$H | Rest], Sum) ->
+ {Sum, Rest};
+holl_len([ N | Rest], Sum) ->
+ NN = N - $0,
+ holl_len(Rest, Sum * 10 + NN).
+holl_len(L) ->
+ holl_len(L,0).
+
+splitlist(L,N) ->
+ {lists:sublist(L,N),lists:nthtail(N,L)}.
+
+hollerith(Str) ->
+ {Len, Rest} = holl_len(Str),
+ splitlist(Rest,Len).
+
+holl_time(Str) ->
+ {Holl,Rest} = hollerith(Str),
+ Rev = lists:reverse(Holl),
+ B = list_to_integer(lists:reverse(lists:sublist(Rev,6))),
+ A = list_to_integer(lists:reverse(lists:nthtail(6,Rev))),
+ {{A,B,0},Rest}.
+
+parse_log(Str) ->
+ {Time, Rest1} = holl_time(Str),
+ {Category,Rest2} = hollerith(Rest1),
+ {Facility,Rest3} = hollerith(Rest2),
+ {Severity,Rest4} = hollerith(Rest3),
+ {Message,_} = hollerith(Rest4),
+ {Time,Category,Facility,Severity,Message}.
diff --git a/lib/os_mon/src/os_mon.app.src b/lib/os_mon/src/os_mon.app.src
new file mode 100644
index 0000000000..15bbd2663c
--- /dev/null
+++ b/lib/os_mon/src/os_mon.app.src
@@ -0,0 +1,32 @@
+%%
+%% %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%
+%%
+
+{application, os_mon,
+ [{description, "CPO CXC 138 46"},
+ {vsn, "%VSN%"},
+ {modules, [os_mon, os_mon_mib, os_sup,
+ disksup, memsup, cpu_sup, os_mon_sysinfo, nteventlog]},
+ {registered, [os_mon_sup, os_mon_sysinfo, disksup, memsup, cpu_sup,
+ os_sup_server]},
+ {applications, [kernel, stdlib, sasl]},
+ {env, [{start_cpu_sup, true},
+ {start_disksup, true},
+ {start_memsup, true},
+ {start_os_sup, false}]},
+ {mod, {os_mon, []}}]}.
diff --git a/lib/os_mon/src/os_mon.appup.src b/lib/os_mon/src/os_mon.appup.src
new file mode 100644
index 0000000000..f8e09a7d87
--- /dev/null
+++ b/lib/os_mon/src/os_mon.appup.src
@@ -0,0 +1,41 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-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%
+%%
+
+{"%VSN%",
+ [
+ {"2.1",
+ [{load_module, cpu_sup},
+ {load_module, disksup},
+ {load_module, memsup},
+ {load_module, os_mon},
+ {load_module, os_mon_mib}]},
+ {"2.1.1",
+ [{load_module, os_mon_mib}]}
+ ],
+ [
+ {"2.1",
+ [{load_module, cpu_sup},
+ {load_module, disksup},
+ {load_module, memsup},
+ {load_module, os_mon},
+ {load_module, os_mon_mib}]},
+ {"2.1.1",
+ [{load_module, os_mon_mib}]}
+ ]
+}.
diff --git a/lib/os_mon/src/os_mon.erl b/lib/os_mon/src/os_mon.erl
new file mode 100644
index 0000000000..ef368571db
--- /dev/null
+++ b/lib/os_mon/src/os_mon.erl
@@ -0,0 +1,179 @@
+%%
+%% %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%
+%%
+-module(os_mon).
+
+-behaviour(application).
+-behaviour(supervisor).
+
+%% API
+-export([call/2, call/3, get_env/2]).
+
+%% Application callbacks
+-export([start/2, stop/1]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+%%%-----------------------------------------------------------------
+%%% API
+%%%-----------------------------------------------------------------
+
+call(Service, Request) ->
+ call(Service, Request, 5000).
+
+call(Service, Request, Timeout) ->
+ try gen_server:call(server_name(Service), Request, Timeout)
+ catch
+ exit:{noproc, Call} ->
+ case lists:keysearch(os_mon, 1,
+ application:which_applications()) of
+ {value, _AppInfo} ->
+ case startp(Service) of
+ true ->
+ erlang:exit({noproc, Call});
+ false ->
+ String = "OS_MON (~p) called by ~p, "
+ "unavailable~n",
+ error_logger:warning_msg(String,
+ [Service, self()]),
+ Service:dummy_reply(Request)
+ end;
+ false ->
+ String = "OS_MON (~p) called by ~p, not started~n",
+ error_logger:warning_msg(String, [Service, self()]),
+ Service:dummy_reply(Request)
+ end
+ end.
+
+get_env(Service, Param) ->
+ case application:get_env(os_mon, Param) of
+ {ok, Value} ->
+ case Service:param_type(Param, Value) of
+ true ->
+ Value;
+ false ->
+ String = "OS_MON (~p), ignoring "
+ "bad configuration parameter (~p=~p)~n"
+ "Using default value instead~n",
+ error_logger:warning_msg(String,
+ [Service, Param, Value]),
+ Service:param_default(Param)
+ end;
+ undefined ->
+ Service:param_default(Param)
+ end.
+
+%%%-----------------------------------------------------------------
+%%% Application callbacks
+%%%-----------------------------------------------------------------
+
+start(_, _) ->
+ supervisor:start_link({local, os_mon_sup}, os_mon, []).
+
+stop(_) ->
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% Supervisor callbacks
+%%%-----------------------------------------------------------------
+
+init([]) ->
+ SupFlags = case os:type() of
+ {win32, _} ->
+ {one_for_one, 5, 3600};
+ _ ->
+ {one_for_one, 4, 3600}
+ end,
+ SysInf = childspec(sysinfo, startp(sysinfo)),
+ DskSup = childspec(disksup, startp(disksup)),
+ MemSup = childspec(memsup, startp(memsup)),
+ CpuSup = childspec(cpu_sup, startp(cpu_sup)),
+ OsSup = childspec(os_sup, startp(os_sup)),
+ {ok, {SupFlags, SysInf ++ DskSup ++ MemSup ++ CpuSup ++ OsSup}}.
+
+childspec(_Service, false) ->
+ [];
+childspec(cpu_sup, true) ->
+ [{cpu_sup, {cpu_sup, start_link, []},
+ permanent, 2000, worker, [cpu_sup]}];
+childspec(disksup, true) ->
+ [{disksup, {disksup, start_link, []},
+ permanent, 2000, worker, [disksup]}];
+childspec(memsup, true) ->
+ [{memsup, {memsup, start_link, []},
+ permanent, 2000, worker, [memsup]}];
+childspec(os_sup, true) ->
+ OS = os:type(),
+ Mod = case OS of
+ {win32, _} -> nteventlog; % windows
+ _ -> os_sup % solaris
+ end,
+ [{os_sup, {os_sup, start_link, [OS]},
+ permanent, 10000, worker, [Mod]}];
+childspec(sysinfo, true) ->
+ [{os_mon_sysinfo, {os_mon_sysinfo, start_link, []},
+ permanent, 2000, worker, [os_mon_sysinfo]}].
+
+%%%-----------------------------------------------------------------
+%%% Internal functions (OS_Mon configuration)
+%%%-----------------------------------------------------------------
+
+startp(Service) ->
+ %% Available for this platform?
+ case lists:member(Service, services(os:type())) of
+ true ->
+ %% Is there a start configuration parameter?
+ case start_param(Service) of
+ none ->
+ true;
+ Param ->
+ %% Is the start configuration parameter 'true'?
+ case application:get_env(os_mon, Param) of
+ {ok, true} ->
+ true;
+ _ ->
+ false
+ end
+ end;
+ false ->
+ false
+ end.
+
+services({unix, sunos}) ->
+ [cpu_sup, disksup, memsup, os_sup];
+services({unix, _}) -> % Other unix.
+ [cpu_sup, disksup, memsup];
+services({win32, _}) ->
+ [disksup, memsup, os_sup, sysinfo];
+services(vxworks) ->
+ [memsup];
+services(_) ->
+ [].
+
+server_name(cpu_sup) -> cpu_sup;
+server_name(disksup) -> disksup;
+server_name(memsup) -> memsup;
+server_name(os_sup) -> os_sup_server;
+server_name(sysinfo) -> os_mon_sysinfo.
+
+start_param(cpu_sup) -> start_cpu_sup;
+start_param(disksup) -> start_disksup;
+start_param(memsup) -> start_memsup;
+start_param(os_sup) -> start_os_sup;
+start_param(sysinfo) -> none.
diff --git a/lib/os_mon/src/os_mon_mib.erl b/lib/os_mon/src/os_mon_mib.erl
new file mode 100644
index 0000000000..a4ce274a16
--- /dev/null
+++ b/lib/os_mon/src/os_mon_mib.erl
@@ -0,0 +1,250 @@
+%%
+%% %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%
+%%
+-module(os_mon_mib).
+%%%-----------------------------------------------------------------
+%%% Description: This module implements the OS-MON-MIB.
+%%% The tables are implemented as shadow tables with the module
+%%% snmp_shadow_table. Here the update functions are implemented.
+%%%-----------------------------------------------------------------
+
+-include("../../otp_mibs/include/OTP-MIB.hrl").
+
+%% API
+-export([load/1, unload/1]).
+
+%% Deprecated API
+-export([init/1, stop/1]).
+
+-deprecated([{init,1,eventually},
+ {stop,1,eventually}]).
+
+%% SNMP instrumentation
+-export([load_table/1, load_table/3, disk_table/1, disk_table/3,
+ mem_sys_mark/1, mem_proc_mark/1, disk_threshold/1]).
+
+%% SNMP shadow functions
+-export([update_load_table/0, update_disk_table/0]).
+
+%% Exported for internal use via rpc
+-export([get_load/1, get_disks/1]).
+
+%% Shadow tables
+-record(loadTable, {
+ loadErlNodeName,
+ loadSystemTotalMemory,
+ loadSystemUsedMemory,
+ loadLargestErlProcess,
+ loadLargestErlProcessUsedMemory,
+ loadCpuLoad,
+ loadCpuLoad5,
+ loadCpuLoad15,
+ loadOsWordsize,
+ loadSystemTotalMemory64,
+ loadSystemUsedMemory64,
+ loadLargestErlProcessUsedMemory64}).
+
+-record(diskTable,
+ {key, diskDescr, diskKBytes, diskCapacity}).
+
+%% Shadow argument macros
+-define(loadShadowArgs,
+ {loadTable, string, record_info(fields, loadTable), 5000,
+ {os_mon_mib, update_load_table}}).
+
+-define(diskShadowArgs,
+ {diskTable, {integer, integer}, record_info(fields, diskTable), 5000,
+ {os_mon_mib, update_disk_table}}).
+
+%% Misc
+-record(diskAlloc, {diskDescr, diskId}).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+
+%%-------------------------------------------------------------------------
+%% load(Agent) -> ok | {error, Reason}
+%% Agent - pid() | atom()
+%% Reason - term()
+%% Description: Loads the OTP-OS-MON-MIB
+%%-------------------------------------------------------------------------
+load(Agent) ->
+ MibDir = filename:join(code:priv_dir(os_mon), "mibs"),
+ snmpa:load_mibs(Agent, [filename:join(MibDir, "OTP-OS-MON-MIB")]).
+
+%%-------------------------------------------------------------------------
+%% unload(Agent) -> ok | {error, Reason}
+%% Agent - pid() | atom()
+%% Reason - term()
+%% Description: Unloads the OTP-OS-MON-MIB
+%%-------------------------------------------------------------------------
+unload(Agent) ->
+ snmpa:unload_mibs(Agent, ["OTP-OS-MON-MIB"]).
+
+%% To be backwards compatible
+init(Agent) ->
+ load(Agent).
+stop(Agent) ->
+ unload(Agent).
+
+%%%=========================================================================
+%%% SNMP instrumentation
+%%%=========================================================================
+load_table(Op) ->
+ snmp_shadow_table:table_func(Op, ?loadShadowArgs).
+load_table(Op, RowIndex, Cols) ->
+ snmp_shadow_table:table_func(Op, RowIndex, Cols, ?loadShadowArgs).
+
+disk_table(new) ->
+ Tab = diskAlloc,
+ Storage = ram_copies,
+ case lists:member(Tab, mnesia:system_info(tables)) of
+ true ->
+ case mnesia:table_info(Tab, storage_type) of
+ unknown ->
+ {atomic, ok}=mnesia:add_table_copy(Tab, node(), Storage);
+ Storage ->
+ catch delete_all(Tab)
+ end;
+ false ->
+ Nodes = [node()],
+ Props = [{type, set},
+ {attributes, record_info(fields, diskAlloc)},
+ {local_content, true},
+ {Storage, Nodes}],
+ {atomic, ok} = mnesia:create_table(Tab, Props)
+
+ end,
+ Rec = #diskAlloc{diskDescr = next_index, diskId = 1},
+ ok = mnesia:dirty_write(Rec),
+ snmp_shadow_table:table_func(new, ?diskShadowArgs).
+
+disk_table(Op, RowIndex, Cols) ->
+ snmp_shadow_table:table_func(Op, RowIndex, Cols, ?diskShadowArgs).
+
+mem_sys_mark(get) ->
+ {value, memsup:get_sysmem_high_watermark()};
+mem_sys_mark(_) ->
+ ok.
+
+mem_proc_mark(get) ->
+ {value, memsup:get_procmem_high_watermark()};
+mem_proc_mark(_) ->
+ ok.
+
+disk_threshold(get) ->
+ {value, disksup:get_almost_full_threshold()};
+disk_threshold(_) ->
+ ok.
+
+%%%=========================================================================
+%%% SNMP shadow functions
+%%%=========================================================================
+update_load_table() ->
+ delete_all(loadTable),
+ lists:foreach(
+ fun(Node) ->
+ case rpc:call(Node, os_mon_mib, get_load, [Node]) of
+ Load when is_record(Load,loadTable) ->
+ ok = mnesia:dirty_write(Load);
+ _Else ->
+ ok
+ end
+ end, [node() | nodes()]).
+
+
+update_disk_table() ->
+ delete_all(diskTable),
+ node_update_disk_table(
+ otp_mib:erl_node_table(get_next, [], [?erlNodeName,?erlNodeOutBytes])).
+
+%%%========================================================================
+%%% Exported for internal use via rpc
+%%%========================================================================
+get_load(Node) ->
+ {Total, Allocated, PidString, PidAllocated} = case memsup:get_memory_data() of
+ {MemTot, MemAlloc, undefined} -> {MemTot, MemAlloc, "undefined", 0};
+ {MemTot, MemAlloc, {Pid, PidMem}} -> {MemTot, MemAlloc, pid_to_str(Pid), PidMem}
+ end,
+ OsWordsize = case memsup:get_os_wordsize() of
+ WS when is_integer(WS) -> WS;
+ _ -> 0
+ end,
+ #loadTable{
+ loadErlNodeName = atom_to_list(Node),
+ loadSystemTotalMemory = mask_int32(Total),
+ loadSystemUsedMemory = mask_int32(Allocated),
+ loadLargestErlProcess = PidString,
+ loadLargestErlProcessUsedMemory = mask_int32(PidAllocated),
+ loadCpuLoad = get_cpu_load(avg1),
+ loadCpuLoad5 = get_cpu_load(avg5),
+ loadCpuLoad15 = get_cpu_load(avg15),
+ loadOsWordsize = OsWordsize,
+ loadSystemTotalMemory64 = Total,
+ loadSystemUsedMemory64 = Allocated,
+ loadLargestErlProcessUsedMemory64 = PidAllocated
+ }.
+
+mask_int32(Value) -> Value band ((1 bsl 32) - 1).
+
+get_disks(NodeId) ->
+ element(1,
+ lists:mapfoldl(
+ fun({Descr, KByte, Capacity}, DiskId) ->
+ {#diskTable{key = {NodeId, DiskId},
+ diskDescr = Descr,
+ diskKBytes = KByte,
+ diskCapacity = Capacity},
+ DiskId + 1}
+ end, 1, disksup:get_disk_data())).
+
+
+%%%========================================================================
+%%% Internal functions
+%%%========================================================================
+node_update_disk_table([_, endOfTable]) ->
+ ok;
+
+node_update_disk_table([{[?erlNodeName | IndexList], NodeStr}, _]) ->
+ Disks = rpc:call(list_to_atom(NodeStr), os_mon_mib, get_disks,
+ IndexList),
+ lists:foreach(fun(Disk) ->
+ mnesia:dirty_write(Disk)
+ end, Disks),
+ node_update_disk_table(otp_mib:erl_node_table(get_next,
+ IndexList,
+ [?erlNodeName,
+ ?erlNodeOutBytes])).
+
+get_cpu_load(X) when X == avg1; X == avg5; X == avg15 ->
+ case erlang:round(apply(cpu_sup, X, [])/2.56) of
+ Large when Large > 100 ->
+ 100;
+ Load ->
+ Load
+ end.
+
+delete_all(Name) -> delete_all(mnesia:dirty_first(Name), Name).
+delete_all('$end_of_table', _Name) -> done;
+delete_all(Key, Name) ->
+ Next = mnesia:dirty_next(Name, Key),
+ ok = mnesia:dirty_delete({Name, Key}),
+ delete_all(Next, Name).
+
+pid_to_str(Pid) -> lists:flatten(io_lib:format("~w", [Pid])).
diff --git a/lib/os_mon/src/os_mon_sysinfo.erl b/lib/os_mon/src/os_mon_sysinfo.erl
new file mode 100644
index 0000000000..5d12bd95d1
--- /dev/null
+++ b/lib/os_mon/src/os_mon_sysinfo.erl
@@ -0,0 +1,147 @@
+%%
+%% %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%
+%%
+-module(os_mon_sysinfo).
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0]).
+-export([get_disk_info/0, get_disk_info/1, get_mem_info/0]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(DISK_INFO, $d).
+-define(MEM_INFO, $m).
+-define(OK, $o).
+
+-record(state, {port}).
+
+%%----------------------------------------------------------------------
+%% API
+%%----------------------------------------------------------------------
+
+start_link() ->
+ gen_server:start_link({local,os_mon_sysinfo}, os_mon_sysinfo, [],[]).
+
+get_disk_info() ->
+ gen_server:call(os_mon_sysinfo, get_disk_info).
+
+get_disk_info(DriveRoot) ->
+ gen_server:call(os_mon_sysinfo, {get_disk_info,DriveRoot}).
+
+get_mem_info() ->
+ gen_server:call(os_mon_sysinfo, get_mem_info).
+
+%%----------------------------------------------------------------------
+%% gen_server callbacks
+%%----------------------------------------------------------------------
+
+init([]) ->
+ process_flag(trap_exit, true),
+ process_flag(priority, low),
+ Port = case os:type() of
+ {win32, _OSname} -> start_portprogram();
+ OS -> exit({unsupported_os, OS})
+ end,
+ {ok, #state{port=Port}}.
+
+handle_call(get_disk_info, _From, State) ->
+ {reply, get_disk_info1(State#state.port), State};
+handle_call({get_disk_info,RootList}, _From, State) ->
+ {reply, get_disk_info1(State#state.port,RootList), State};
+handle_call(get_mem_info, _From, State) ->
+ {reply, get_mem_info1(State#state.port), State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info({'EXIT', _Port, Reason}, State) ->
+ {stop, {port_died, Reason}, State#state{port=not_used}};
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, State) ->
+ case State#state.port of
+ not_used ->
+ ok;
+ Port ->
+ port_close(Port)
+ end,
+ ok.
+
+%% os_mon-2.0
+%% For live downgrade to/upgrade from os_mon-1.8[.1]
+code_change(Vsn, PrevState, "1.8") ->
+ case Vsn of
+
+ %% Downgrade from this version
+ {down, _Vsn} ->
+ process_flag(trap_exit, false);
+
+ %% Upgrade to this version
+ _Vsn ->
+ process_flag(trap_exit, true)
+ end,
+ {ok, PrevState};
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%----------------------------------------------------------------------
+%% Internal functions
+%%----------------------------------------------------------------------
+
+start_portprogram() ->
+ Command =
+ filename:join([code:priv_dir(os_mon),"bin","win32sysinfo.exe"]),
+ Port = open_port({spawn,Command}, [{packet,1}]),
+ receive
+ {Port, {data, [?OK]}} ->
+ Port;
+ {Port, {data, Data}} ->
+ exit({port_error, Data});
+ {'EXIT', Port, Reason} ->
+ exit({port_died, Reason})
+ after 5000 ->
+ exit({port_error, timeout})
+ end.
+
+get_disk_info1(Port) ->
+ Port ! {self(),{command,[?DISK_INFO]}},
+ get_data(Port,[]).
+
+get_disk_info1(Port,PathList) ->
+ Port ! {self(),{command,[?DISK_INFO|[P++[0]||P <- PathList]]}},
+ get_data(Port,[]).
+
+get_mem_info1(Port) ->
+ Port ! {self(),{command,[?MEM_INFO]}},
+ get_data(Port,[]).
+
+get_data(Port, Sofar) ->
+ receive
+ {Port, {data, [?OK]}} ->
+ lists:reverse(Sofar);
+ {Port, {data, Bytes}} ->
+ get_data(Port, [Bytes|Sofar]);
+ {'EXIT', Port, Reason} ->
+ exit({port_died, Reason})
+ after 5000 ->
+ lists:reverse(Sofar)
+ end.
diff --git a/lib/os_mon/src/os_sup.erl b/lib/os_mon/src/os_sup.erl
new file mode 100644
index 0000000000..f5c6c138ba
--- /dev/null
+++ b/lib/os_mon/src/os_sup.erl
@@ -0,0 +1,258 @@
+%%
+%% %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%
+%%
+-module(os_sup).
+-behaviour(gen_server).
+
+%% API
+-export([start_link/1, start/0, stop/0]).
+-export([error_report/2]).
+-export([enable/0, enable/2, disable/0, disable/2]).
+-export([param_type/2, param_default/1]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-record(state, {port, mfa, config, path, conf}).
+
+%%----------------------------------------------------------------------
+%% API
+%%----------------------------------------------------------------------
+
+start_link({win32, _OSname}) ->
+ Identifier = os_sup,
+ MFA = os_mon:get_env(os_sup, os_sup_mfa),
+ gen_server:start_link({local, os_sup_server}, nteventlog,
+ [Identifier, MFA], []);
+start_link(_OS) ->
+ gen_server:start_link({local, os_sup_server}, os_sup, [], []).
+
+start() -> % for testing
+ gen_server:start({local, os_sup_server}, os_sup, [], []).
+
+stop() ->
+ gen_server:call(os_sup_server, stop).
+
+error_report(LogData, Tag) ->
+ error_logger:error_report(Tag, LogData).
+
+enable() ->
+ command(enable).
+enable(Path, Conf) ->
+ command(enable, Path, Conf).
+
+disable() ->
+ command(disable).
+disable(Path, Conf) ->
+ command(disable, Path, Conf).
+
+param_type(os_sup_errortag, Val) when is_atom(Val) -> true;
+param_type(os_sup_own, Val) -> io_lib:printable_list(Val);
+param_type(os_sup_syslogconf, Val) -> io_lib:printable_list(Val);
+param_type(os_sup_enable, Val) when Val==true; Val==false -> true;
+param_type(os_sup_mfa, {Mod,Func,Args}) when is_atom(Mod),
+ is_atom(Func),
+ is_list(Args) -> true;
+param_type(_Param, _Val) -> false.
+
+param_default(os_sup_errortag) -> std_error;
+param_default(os_sup_own) -> "/etc";
+param_default(os_sup_syslogconf) -> "/etc/syslog.conf";
+param_default(os_sup_enable) -> true;
+param_default(os_sup_mfa) -> {os_sup, error_report, [std_error]}.
+
+%%----------------------------------------------------------------------
+%% gen_server callbacks
+%%----------------------------------------------------------------------
+
+init([]) ->
+ process_flag(trap_exit, true),
+ process_flag(priority, low),
+
+ case os:type() of
+ {unix, sunos} ->
+ init2();
+ OS -> {stop, {unsupported_os, OS}}
+ end.
+
+init2() -> % Enable service if configured to do so
+ ConfigP = os_mon:get_env(os_sup, os_sup_enable),
+ case ConfigP of
+ true -> % ..yes -- do enable
+ Path = os_mon:get_env(os_sup, os_sup_own),
+ Conf = os_mon:get_env(os_sup, os_sup_syslogconf),
+ case enable(Path, Conf) of
+ ok ->
+ init3(#state{config=ConfigP, path=Path, conf=Conf});
+ {error, Error} ->
+ {stop, {mod_syslog, Error}}
+ end;
+ false -> % ..no -- skip directly to init3/1
+ init3(#state{config=ConfigP})
+ end.
+
+init3(State0) ->
+ Port = start_portprogram(),
+
+ %% Read the values of some configuration parameters
+ MFA = case os_mon:get_env(os_sup, os_sup_mfa) of
+ {os_sup, error_report, _} ->
+ Tag = os_mon:get_env(os_sup, os_sup_errortag),
+ {os_sup, error_report, [Tag]};
+ MFA0 ->
+ MFA0
+ end,
+
+ {ok, State0#state{port=Port, mfa=MFA}}.
+
+handle_call(stop, _From, State) ->
+ {stop, normal, ok, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info({_Port, {data, Data}}, #state{mfa={M,F,A}} = State) ->
+ apply(M, F, [Data | A]),
+ {noreply, State};
+handle_info({'EXIT', _Port, Reason}, State) ->
+ {stop, {port_died, Reason}, State#state{port=not_used}};
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, #state{port=Port} = State) ->
+ case State#state.config of
+ true when is_port(Port) ->
+ Port ! {self(), {command, "only_stdin"}},
+ Res = disable(State#state.path, State#state.conf),
+ port_close(Port),
+ if
+ Res/="0" -> exit({mod_syslog, Res});
+ true -> ok
+ end;
+ true ->
+ Res = disable(State#state.path, State#state.conf),
+ if
+ Res/="0" -> exit({mod_syslog, Res});
+ true -> ok
+ end;
+ false when is_port(Port) ->
+ Port ! {self(), {command, "only_stdin"}},
+ port_close(Port);
+ false ->
+ ok
+ end.
+
+%% os_mon-2.0
+%% For live downgrade to/upgrade from os_mon-1.8[.1]
+code_change(Vsn, PrevState, "1.8") ->
+ case Vsn of
+
+ %% Downgrade from this version
+ {down, _Vsn} ->
+
+ %% Find out the error tag used
+ {DefM, DefF, _} = param_default(os_sup_mfa),
+ Tag = case PrevState#state.mfa of
+
+ %% Default callback function is used, then use
+ %% the corresponding tag
+ {DefM, DefF, [Tag0]} ->
+ Tag0;
+
+ %% Default callback function is *not* used
+ %% (before the downgrade, that is)
+ %% -- check the configuration parameter
+ _ ->
+ case application:get_env(os_mon,
+ os_sup_errortag) of
+ {ok, Tag1} ->
+ Tag1;
+
+ %% (actually, if it has no value,
+ %% the process should terminate
+ %% according to 1.8.1 version, but that
+ %% seems too harsh here)
+ _ ->
+ std_error
+ end
+ end,
+
+ %% Downgrade to old state record
+ State = {state, PrevState#state.port, Tag},
+ {ok, State};
+
+ %% Upgrade to this version
+ _Vsn ->
+
+ {state, Port, Tag} = PrevState,
+
+ {DefM, DefF, _} = param_default(os_sup_mfa),
+ MFA = {DefM, DefF, [Tag]},
+
+ %% We can safely assume the following configuration
+ %% parameters are defined, otherwise os_sup would never had
+ %% started in the first place.
+ %% (We can *not* safely assume they haven't been changed,
+ %% but that's a weakness inherited from the 1.8.1 version)
+ Path = application:get_env(os_mon, os_sup_own),
+ Conf = application:get_env(os_mon, os_sup_syslogconf),
+
+ %% Upgrade to this state record
+ State = #state{port=Port, mfa=MFA, config=true,
+ path=Path, conf=Conf},
+ {ok, State}
+ end;
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%----------------------------------------------------------------------
+%% Internal functions
+%%----------------------------------------------------------------------
+
+start_portprogram() ->
+ OwnPath = os_mon:get_env(os_sup, os_sup_own),
+ Command =
+ filename:join([code:priv_dir(os_mon), "bin", "ferrule"]) ++
+ " " ++ OwnPath,
+ open_port({spawn, Command}, [{packet, 2}]).
+
+%% os:cmd(cmd_str(enable)) should be done BEFORE starting os_sup
+%% os:cmd(cmd_str(disable)) should be done AFTER os_sup is terminated
+%% Both commands return "0" if successful
+command(Mode) ->
+ command(Mode, "/etc", "/etc/syslog.conf").
+command(Mode, Path, Conf) ->
+ case os:cmd(cmd_str(Mode, Path, Conf)) of
+ "0" ->
+ ok;
+ Error ->
+ {error, Error}
+ end.
+
+cmd_str(Mode, Path, Conf) ->
+ %% modpgm modesw ownpath syslogconf
+ PrivDir = code:priv_dir(os_mon),
+ ModeSw =
+ case Mode of
+ enable ->
+ " otp ";
+ disable ->
+ " nootp "
+ end,
+ PrivDir ++ "/bin/mod_syslog" ++ ModeSw ++ Path ++ " " ++ Conf.
diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk
new file mode 100644
index 0000000000..d9f56416bf
--- /dev/null
+++ b/lib/os_mon/vsn.mk
@@ -0,0 +1 @@
+OS_MON_VSN = 2.2.4