summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerald Carter <jerry@samba.org>2005-11-08 16:33:45 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 11:05:21 -0500
commit77460a90756dcaa54ec12bbcd30a5266286103d7 (patch)
treeaac95461d5cf4b3c0d9dc450bb78e8c2f6fef2d9
parent38b54d063d7fd262902324e5da15205542878adf (diff)
downloadsamba-77460a90756dcaa54ec12bbcd30a5266286103d7.tar.gz
samba-77460a90756dcaa54ec12bbcd30a5266286103d7.tar.bz2
samba-77460a90756dcaa54ec12bbcd30a5266286103d7.zip
r11579: syncing up perf counter code cfrom trunk
(This used to be commit 59c00924b67aa3d37a933731a56d03963ec7f1b5)
-rw-r--r--examples/perfcounter/Makefile38
-rw-r--r--examples/perfcounter/perf.h196
-rw-r--r--examples/perfcounter/perf_writer.c214
-rw-r--r--examples/perfcounter/perf_writer_cpu.c190
-rw-r--r--examples/perfcounter/perf_writer_disk.c225
-rw-r--r--examples/perfcounter/perf_writer_mem.c125
-rw-r--r--examples/perfcounter/perf_writer_process.c86
-rw-r--r--examples/perfcounter/perf_writer_util.c244
-rwxr-xr-xexamples/perfcounter/perfcountd.init66
-rw-r--r--source3/param/loadparm.c3
-rw-r--r--source3/registry/reg_frontend.c1
-rw-r--r--source3/registry/reg_perfcount.c214
-rw-r--r--source3/services/services_db.c1
-rw-r--r--source3/utils/net_rpc_registry.c14
14 files changed, 1568 insertions, 49 deletions
diff --git a/examples/perfcounter/Makefile b/examples/perfcounter/Makefile
new file mode 100644
index 0000000000..07bc7657c9
--- /dev/null
+++ b/examples/perfcounter/Makefile
@@ -0,0 +1,38 @@
+#
+# Copyright (C) Marcin Krzysztof Porwit 2005
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+SAMBA_SRC_DIR=../../source
+TDB_SRC_DIR=$(SAMBA_SRC_DIR)/tdb
+
+CFLAGS = -g -I$(SAMBA_SRC_DIR)/include -I$(TDB_SRC_DIR)
+CC = gcc
+
+PROGS = perfcount
+TDB_OBJ = $(TDB_SRC_DIR)/tdb.o $(TDB_SRC_DIR)/spinlock.o $(TDB_SRC_DIR)/tdbback.o
+PERF_WRITER_OBJ = perf_writer.o perf_writer_mem.o perf_writer_util.o perf_writer_cpu.o perf_writer_process.o perf_writer_disk.o
+
+default: $(PROGS)
+
+$(TDB_OBJ):
+ cd $(TDB_SRC_DIR) && make
+
+perfcount: $(PERF_WRITER_OBJ) $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o perfcount $(PERF_WRITER_OBJ) $(TDB_OBJ)
+
+clean:
+ rm -f $(PROGS) *.o *~ *% core
diff --git a/examples/perfcounter/perf.h b/examples/perfcounter/perf.h
new file mode 100644
index 0000000000..7279e78831
--- /dev/null
+++ b/examples/perfcounter/perf.h
@@ -0,0 +1,196 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Performance Counter Daemon
+ *
+ * Copyright (C) Marcin Krzysztof Porwit 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __PERF_H__
+#define __PERF_H__
+
+#include <stdlib.h>
+#include <time.h>
+#include <math.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <limits.h>
+#include "tdb.h"
+#include <rpc_perfcount_defs.h>
+#include <sys/statfs.h>
+#include <sys/times.h>
+#include <sys/sysinfo.h>
+
+#define NUM_COUNTERS 10
+
+#define NAME_LEN 256
+#define HELP_LEN 1024
+
+#define PERF_OBJECT 0
+#define PERF_INSTANCE 1
+#define PERF_COUNTER 2
+
+#define FALSE 0
+#define TRUE !FALSE
+
+#define PROC_BUF 256
+#define LARGE_BUF 16384
+
+typedef struct perf_counter
+{
+ int index;
+ char name[NAME_LEN];
+ char help[HELP_LEN];
+ char relationships[NAME_LEN];
+ unsigned int counter_type;
+ int record_type;
+} PerfCounter;
+
+typedef struct mem_data
+{
+ unsigned int availPhysKb;
+ unsigned int availSwapKb;
+ unsigned int totalPhysKb;
+ unsigned int totalSwapKb;
+} MemData;
+
+typedef struct mem_info
+{
+ PerfCounter memObjDesc;
+ PerfCounter availPhysKb;
+ PerfCounter availSwapKb;
+ PerfCounter totalPhysKb;
+ PerfCounter totalSwapKb;
+ MemData *data;
+} MemInfo;
+
+typedef struct cpu_data
+{
+ unsigned long long user;
+ unsigned long long nice;
+ unsigned long long system;
+ unsigned long long idle;
+} CPUData;
+
+typedef struct cpu_info
+{
+ unsigned int numCPUs;
+ PerfCounter cpuObjDesc;
+ PerfCounter userCPU;
+ PerfCounter niceCPU;
+ PerfCounter systemCPU;
+ PerfCounter idleCPU;
+ CPUData *data;
+} CPUInfo;
+
+typedef struct disk_meta_data
+{
+ char name[NAME_LEN];
+ char mountpoint[NAME_LEN];
+} DiskMetaData;
+
+typedef struct disk_data
+{
+ unsigned long long freeMegs;
+ unsigned int writesPerSec;
+ unsigned int readsPerSec;
+} DiskData;
+
+typedef struct disk_info
+{
+ unsigned int numDisks;
+ DiskMetaData *mdata;
+ PerfCounter diskObjDesc;
+ PerfCounter freeMegs;
+ PerfCounter writesPerSec;
+ PerfCounter readsPerSec;
+ DiskData *data;
+} DiskInfo;
+
+typedef struct process_data
+{
+ unsigned int runningProcessCount;
+} ProcessData;
+
+typedef struct process_info
+{
+ PerfCounter processObjDesc;
+ PerfCounter runningProcessCount;
+ ProcessData *data;
+} ProcessInfo;
+
+typedef struct perf_data_block
+{
+ unsigned int counter_id;
+ unsigned int num_counters;
+ unsigned int NumObjectTypes;
+ unsigned long long PerfTime;
+ unsigned long long PerfFreq;
+ unsigned long long PerfTime100nSec;
+ MemInfo memInfo;
+ CPUInfo cpuInfo;
+ ProcessInfo processInfo;
+ DiskInfo diskInfo;
+} PERF_DATA_BLOCK;
+
+typedef struct runtime_settings
+{
+ /* Runtime flags */
+ int dflag;
+ /* DB path names */
+ char dbDir[PATH_MAX];
+ char nameFile[PATH_MAX];
+ char counterFile[PATH_MAX];
+ /* TDB context */
+ TDB_CONTEXT *cnames;
+ TDB_CONTEXT *cdata;
+} RuntimeSettings;
+
+/* perf_writer_ng_util.c function prototypes */
+void fatal(char *msg);
+void add_key(TDB_CONTEXT *db, char *keystring, char *datastring, int flags);
+void add_key_raw(TDB_CONTEXT *db, char *keystring, void *datastring, size_t datasize, int flags);
+void make_key(char *buf, int buflen, int key_part1, char *key_part2);
+void parse_flags(RuntimeSettings *rt, int argc, char **argv);
+void setup_file_paths(RuntimeSettings *rt);
+void daemonize(RuntimeSettings *rt);
+
+/* perf_writer_ng_mem.c function prototypes */
+void get_meminfo(PERF_DATA_BLOCK *data);
+void init_memdata_desc(PERF_DATA_BLOCK *data);
+void init_memdata(PERF_DATA_BLOCK *data);
+void output_mem_desc(PERF_DATA_BLOCK *data, RuntimeSettings rt);
+void output_meminfo(PERF_DATA_BLOCK *data, RuntimeSettings rt, int tdb_flags);
+void init_perf_counter(PerfCounter *counter, PerfCounter *parent, unsigned int index, char *name, char *help, int counter_type, int record_type);
+
+/* perf_writer_ng_cpu.c function prototypes */
+unsigned long long get_cpufreq();
+void init_cpudata_desc(PERF_DATA_BLOCK *data);
+void get_cpuinfo(PERF_DATA_BLOCK *data);
+void init_cpu_data(PERF_DATA_BLOCK *data);
+void output_cpu_desc(PERF_DATA_BLOCK *data, RuntimeSettings rt);
+void output_cpuinfo(PERF_DATA_BLOCK *data, RuntimeSettings rt, int tdb_flags);
+
+#endif /* __PERF_H__ */
diff --git a/examples/perfcounter/perf_writer.c b/examples/perfcounter/perf_writer.c
new file mode 100644
index 0000000000..04127f5621
--- /dev/null
+++ b/examples/perfcounter/perf_writer.c
@@ -0,0 +1,214 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Performance Counter Daemon
+ *
+ * Copyright (C) Marcin Krzysztof Porwit 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "perf.h"
+
+sig_atomic_t keep_running = TRUE;
+
+/* allocates memory and gets numCPUs, total memory, and PerfFreq, number of disks... */
+void get_constants(PERF_DATA_BLOCK *data)
+{
+ data->cpuInfo.numCPUs = sysconf(_SC_NPROCESSORS_ONLN);
+ data->PerfFreq = sysconf(_SC_CLK_TCK);
+ init_mem_data(data);
+ init_cpu_data(data);
+ init_process_data(data);
+ init_disk_data(data);
+
+ return;
+}
+
+void output_num_instances(PerfCounter obj, int numInst, RuntimeSettings rt)
+{
+ char key[NAME_LEN];
+ char sdata[NAME_LEN];
+
+ make_key(key, NAME_LEN, obj.index, "inst");
+ memset(sdata, 0, NAME_LEN);
+ sprintf(sdata, "%d", numInst);
+ add_key(rt.cnames, key, sdata, TDB_INSERT);
+
+ return;
+}
+
+void output_perf_desc(PerfCounter counter, RuntimeSettings rt)
+{
+ char key[NAME_LEN];
+ char sdata[NAME_LEN];
+
+ /* First insert the counter name */
+ make_key(key, NAME_LEN, counter.index, NULL);
+ add_key(rt.cnames, key, counter.name, TDB_INSERT);
+ /* Add the help string */
+ make_key(key, NAME_LEN, counter.index + 1, NULL);
+ add_key(rt.cnames, key, counter.help, TDB_INSERT);
+ /* Add the relationships */
+ make_key(key, NAME_LEN, counter.index, "rel");
+ add_key(rt.cnames, key, counter.relationships, TDB_INSERT);
+ /* Add type data if not PERF_OBJECT or PERF_INSTANCE */
+ if(counter.record_type == PERF_COUNTER)
+ {
+ make_key(key, NAME_LEN, counter.index, "type");
+ memset(sdata, 0, NAME_LEN);
+ sprintf(sdata, "%d", counter.counter_type);
+ add_key(rt.cnames, key, sdata, TDB_INSERT);
+ }
+
+ return;
+}
+
+void initialize(PERF_DATA_BLOCK *data, RuntimeSettings *rt, int argc, char **argv)
+{
+ memset(data, 0, sizeof(*data));
+ memset(rt, 0, sizeof(*data));
+
+ parse_flags(rt, argc, argv);
+ setup_file_paths(rt);
+
+ get_constants(data);
+
+ if(rt->dflag == TRUE)
+ daemonize(rt);
+
+ output_mem_desc(data, *rt);
+ output_cpu_desc(data, *rt);
+ output_process_desc(data, *rt);
+ output_disk_desc(data, *rt);
+
+ return;
+}
+
+void refresh_perf_data_block(PERF_DATA_BLOCK *data, RuntimeSettings rt)
+{
+ data->PerfTime100nSec = 0;
+ get_meminfo(data);
+ get_cpuinfo(data);
+ get_processinfo(data);
+ get_diskinfo(data);
+ return;
+}
+
+void output_perf_counter(PerfCounter counter, unsigned long long data,
+ RuntimeSettings rt, int tdb_flags)
+{
+ char key[NAME_LEN];
+ char sdata[NAME_LEN];
+ unsigned int size_mask;
+
+ make_key(key, NAME_LEN, counter.index, NULL);
+ memset(sdata, 0, NAME_LEN);
+
+ size_mask = counter.counter_type & PERF_SIZE_VARIABLE_LEN;
+
+ if(size_mask == PERF_SIZE_DWORD)
+ sprintf(sdata, "%d", (unsigned int)data);
+ else if(size_mask == PERF_SIZE_LARGE)
+ sprintf(sdata, "%Lu", data);
+
+ add_key(rt.cdata, key, sdata, tdb_flags);
+
+ return;
+}
+
+void output_perf_instance(int parentObjInd,
+ int instanceInd,
+ void *instData,
+ size_t dsize,
+ char *name,
+ RuntimeSettings rt,
+ int tdb_flags)
+{
+ char key[NAME_LEN];
+ char sdata[NAME_LEN];
+
+ memset(key, 0, NAME_LEN);
+ sprintf(key, "%di%d", parentObjInd, instanceInd);
+ add_key_raw(rt.cdata, key, instData, dsize, tdb_flags);
+
+ /* encode name */
+ memset(key, 0, NAME_LEN);
+ sprintf(key, "%di%dname", parentObjInd, instanceInd);
+ add_key(rt.cnames, key, name, tdb_flags);
+
+ return;
+}
+
+void output_global_data(PERF_DATA_BLOCK *data, RuntimeSettings rt, int tdb_flags)
+{
+ int i;
+ char key[NAME_LEN];
+ char sdata[NAME_LEN];
+
+ /* Initialize BaseIndex */
+ make_key(key, NAME_LEN, 1, NULL);
+ memset(sdata, 0, NAME_LEN);
+ sprintf(sdata, "%d", data->num_counters);
+ add_key(rt.cnames, key, sdata, tdb_flags);
+ /* Initialize PerfTime, PerfFreq and PerfTime100nSec */
+ memset(sdata, 0, NAME_LEN);
+ make_key(key, NAME_LEN, 0, "PerfTime");
+ sprintf(sdata, "%Lu", data->PerfTime);
+ add_key(rt.cdata, key, sdata, tdb_flags);
+ make_key(key, NAME_LEN, 0, "PerfTime100nSec");
+ memset(sdata, 0, NAME_LEN);
+ sprintf(sdata, "%Lu", data->PerfTime100nSec);
+ add_key(rt.cdata, key, sdata, tdb_flags);
+ memset(sdata, 0, NAME_LEN);
+ make_key(key, NAME_LEN, 0, "PerfFreq");
+ sprintf(sdata, "%Lu", data->PerfFreq);
+ add_key(rt.cnames, key, sdata, tdb_flags);
+
+ return;
+}
+
+void output_perf_data_block(PERF_DATA_BLOCK *data, RuntimeSettings rt, int tdb_flags)
+{
+ output_global_data(data, rt, tdb_flags);
+ output_meminfo(data, rt, tdb_flags);
+ output_cpuinfo(data, rt, tdb_flags);
+ output_processinfo(data, rt, tdb_flags);
+ output_diskinfo(data, rt, tdb_flags);
+ return;
+}
+
+void update_counters(PERF_DATA_BLOCK *data, RuntimeSettings rt)
+{
+ refresh_perf_data_block(data, rt);
+ output_perf_data_block(data, rt, TDB_REPLACE);
+
+ return;
+}
+
+int main(int argc, char **argv)
+{
+ PERF_DATA_BLOCK data;
+ RuntimeSettings rt;
+
+ initialize(&data, &rt, argc, argv);
+
+ while(keep_running)
+ {
+ update_counters(&data, rt);
+ sleep(1);
+ }
+
+ return 0;
+}
diff --git a/examples/perfcounter/perf_writer_cpu.c b/examples/perfcounter/perf_writer_cpu.c
new file mode 100644
index 0000000000..7786c9415e
--- /dev/null
+++ b/examples/perfcounter/perf_writer_cpu.c
@@ -0,0 +1,190 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Performance Counter Daemon
+ *
+ * Copyright (C) Marcin Krzysztof Porwit 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "perf.h"
+
+void init_cpudata_desc(PERF_DATA_BLOCK *data)
+{
+ init_perf_counter(&(data->cpuInfo.cpuObjDesc),
+ &(data->cpuInfo.cpuObjDesc),
+ get_counter_id(data),
+ "Processor",
+ "The Processor object consists of counters that describe the behavior of the CPU.",
+ 0,
+ PERF_OBJECT);
+ init_perf_counter(&(data->cpuInfo.userCPU),
+ &(data->cpuInfo.cpuObjDesc),
+ get_counter_id(data),
+ "\% User CPU Utilization",
+ "\% User CPU Utilization is the percentage of the CPU used by processes executing user code.",
+ PERF_SIZE_LARGE | PERF_TYPE_COUNTER | PERF_COUNTER_RATE | PERF_TIMER_100NS | PERF_DELTA_COUNTER | PERF_DISPLAY_PERCENT,
+ PERF_COUNTER);
+ init_perf_counter(&(data->cpuInfo.systemCPU),
+ &(data->cpuInfo.cpuObjDesc),
+ get_counter_id(data),
+ "\% System CPU Utilization",
+ "\% System CPU Utilization is the percentage of the CPU used by processes doing system calls.",
+ PERF_SIZE_LARGE | PERF_TYPE_COUNTER | PERF_COUNTER_RATE | PERF_TIMER_100NS | PERF_DELTA_COUNTER | PERF_DISPLAY_PERCENT,
+ PERF_COUNTER);
+ init_perf_counter(&(data->cpuInfo.niceCPU),
+ &(data->cpuInfo.cpuObjDesc),
+ get_counter_id(data),
+ "\% Nice CPU Utilization",
+ "\% Nice CPU Utilization is the percentage of the CPU used by processes running in nice mode.",
+ PERF_SIZE_LARGE | PERF_TYPE_COUNTER | PERF_COUNTER_RATE | PERF_TIMER_100NS | PERF_DELTA_COUNTER | PERF_DISPLAY_NOSHOW,
+ PERF_COUNTER);
+ init_perf_counter(&(data->cpuInfo.idleCPU),
+ &(data->cpuInfo.cpuObjDesc),
+ get_counter_id(data),
+ "\% Idle CPU",
+ "\% Idle CPU is the percentage of the CPU not doing any work.",
+ PERF_SIZE_LARGE | PERF_TYPE_COUNTER | PERF_COUNTER_RATE | PERF_TIMER_100NS | PERF_DELTA_COUNTER | PERF_DISPLAY_NOSHOW,
+ PERF_COUNTER);
+
+ return;
+}
+
+void get_cpuinfo(PERF_DATA_BLOCK *data)
+{
+ int num, i;
+ unsigned int cpuid;
+ char buf[PROC_BUF];
+ static FILE *fp = NULL;
+
+ if(!fp)
+ {
+ if(!(fp = fopen("/proc/stat", "r")))
+ {
+ perror("get_cpuinfo: fopen");
+ exit(1);
+ }
+ }
+
+ rewind(fp);
+ fflush(fp);
+
+ /* Read in the first line and discard it -- that has the CPU summary */
+ if(!fgets(buf, sizeof(buf), fp))
+ {
+ perror("get_cpuinfo: fgets");
+ exit(1);
+ }
+ for(i = 0; i < data->cpuInfo.numCPUs; i++)
+ {
+ if(!fgets(buf, sizeof(buf), fp))
+ {
+ perror("get_cpuinfo: fgets");
+ exit(1);
+ }
+ num = sscanf(buf, "cpu%u %Lu %Lu %Lu %Lu",
+ &cpuid,
+ &data->cpuInfo.data[i].user,
+ &data->cpuInfo.data[i].nice,
+ &data->cpuInfo.data[i].system,
+ &data->cpuInfo.data[i].idle);
+ if(i != cpuid)
+ {
+ perror("get_cpuinfo: /proc/stat inconsistent?");
+ exit(1);
+ }
+ /*
+ Alternate way of doing things:
+ struct tms buffer;
+ data->PerfTime100nSec = times(&buffer);
+ */
+ data->PerfTime100nSec += data->cpuInfo.data[i].user +
+ data->cpuInfo.data[i].nice +
+ data->cpuInfo.data[i].system +
+ data->cpuInfo.data[i].idle;
+ }
+ data->PerfTime100nSec /= data->cpuInfo.numCPUs;
+ return;
+}
+
+void init_cpu_data(PERF_DATA_BLOCK *data)
+{
+ data->cpuInfo.data = calloc(data->cpuInfo.numCPUs, sizeof(*data->cpuInfo.data));
+ if(!data->cpuInfo.data)
+ {
+ perror("init_cpu_data: out of memory");
+ exit(1);
+ }
+
+ init_cpudata_desc(data);
+
+ get_cpuinfo(data);
+
+ return;
+}
+
+void output_cpu_desc(PERF_DATA_BLOCK *data, RuntimeSettings rt)
+{
+ output_perf_desc(data->cpuInfo.cpuObjDesc, rt);
+ output_perf_desc(data->cpuInfo.userCPU, rt);
+ output_perf_desc(data->cpuInfo.niceCPU, rt);
+ output_perf_desc(data->cpuInfo.systemCPU, rt);
+ output_perf_desc(data->cpuInfo.idleCPU, rt);
+ if(data->cpuInfo.numCPUs > 1)
+ output_num_instances(data->cpuInfo.cpuObjDesc, data->cpuInfo.numCPUs + 1, rt);
+
+ return;
+}
+
+void output_cpuinfo(PERF_DATA_BLOCK *data, RuntimeSettings rt, int tdb_flags)
+{
+ int i;
+ char buf[NAME_LEN];
+
+ output_perf_counter(data->cpuInfo.userCPU,
+ data->cpuInfo.data[0].user,
+ rt, tdb_flags);
+ output_perf_counter(data->cpuInfo.systemCPU,
+ data->cpuInfo.data[0].system,
+ rt, tdb_flags);
+ output_perf_counter(data->cpuInfo.niceCPU,
+ data->cpuInfo.data[0].nice,
+ rt, tdb_flags);
+ output_perf_counter(data->cpuInfo.idleCPU,
+ data->cpuInfo.data[0].idle,
+ rt, tdb_flags);
+ if(data->cpuInfo.numCPUs > 1)
+ {
+ for(i = 0; i < data->cpuInfo.numCPUs; i++)
+ {
+ memset(buf, 0, NAME_LEN);
+ sprintf(buf, "cpu%d", i);
+ output_perf_instance(data->cpuInfo.cpuObjDesc.index,
+ i,
+ (void *)&(data->cpuInfo.data[i]),
+ sizeof(data->cpuInfo.data[i]),
+ buf, rt, tdb_flags);
+ }
+
+ memset(buf, 0, NAME_LEN);
+ sprintf(buf, "_Total");
+ output_perf_instance(data->cpuInfo.cpuObjDesc.index,
+ i,
+ (void *)&(data->cpuInfo.data[i]),
+ sizeof(data->cpuInfo.data[i]),
+ buf, rt, tdb_flags);
+ }
+ return;
+}
diff --git a/examples/perfcounter/perf_writer_disk.c b/examples/perfcounter/perf_writer_disk.c
new file mode 100644
index 0000000000..b2b4c9690b
--- /dev/null
+++ b/examples/perfcounter/perf_writer_disk.c
@@ -0,0 +1,225 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Performance Counter Daemon
+ *
+ * Copyright (C) Marcin Krzysztof Porwit 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "perf.h"
+
+void init_diskdata_desc(PERF_DATA_BLOCK *data)
+{
+ init_perf_counter(&(data->diskInfo.diskObjDesc),
+ &(data->diskInfo.diskObjDesc),
+ get_counter_id(data),
+ "Logical Disk",
+ "The Logical Disk object consists of counters that show information about disks.",
+ 0,
+ PERF_OBJECT);
+ init_perf_counter(&(data->diskInfo.freeMegs),
+ &(data->diskInfo.diskObjDesc),
+ get_counter_id(data),
+ "Megabytes Free",
+ "The amount of available disk space, in megabytes.",
+ PERF_SIZE_LARGE | PERF_TYPE_NUMBER | PERF_NUMBER_DECIMAL | PERF_DISPLAY_NO_SUFFIX,
+ PERF_COUNTER);
+ init_perf_counter(&(data->diskInfo.writesPerSec),
+ &(data->diskInfo.diskObjDesc),
+ get_counter_id(data),
+ "Writes/sec",
+ "The number of writes per second to that disk.",
+ PERF_SIZE_DWORD | PERF_TYPE_COUNTER | PERF_COUNTER_RATE | PERF_DELTA_COUNTER | PERF_DISPLAY_PER_SEC,
+ PERF_COUNTER);
+ init_perf_counter(&(data->diskInfo.readsPerSec),
+ &(data->diskInfo.diskObjDesc),
+ get_counter_id(data),
+ "Reads/sec",
+ "The number of reads of that disk per second.",
+ PERF_SIZE_DWORD | PERF_TYPE_COUNTER | PERF_COUNTER_RATE | PERF_DELTA_COUNTER | PERF_DISPLAY_PER_SEC,
+ PERF_COUNTER);
+
+ return;
+}
+void init_num_disks(PERF_DATA_BLOCK *data)
+{
+ FILE *mtab;
+ char buf[PROC_BUF];
+ char *start, *stop;
+ int i = 0, num;
+
+ if(!(mtab = fopen("/etc/mtab", "r")))
+ {
+ perror("init_disk_names: fopen");
+ exit(1);
+ }
+
+ rewind(mtab);
+ fflush(mtab);
+
+ while(fgets(buf, sizeof(buf), mtab))
+ {
+ if(start = strstr(buf, "/dev/"))
+ {
+ if(start = strstr(start, "da"))
+ {
+ i++;
+ }
+ }
+ }
+
+ data->diskInfo.numDisks = i;
+ fclose(mtab);
+
+ return;
+}
+
+void init_disk_names(PERF_DATA_BLOCK *data)
+{
+ FILE *mtab;
+ char buf[PROC_BUF];
+ char *start, *stop;
+ int i = 0, num;
+
+ if(!(mtab = fopen("/etc/mtab", "r")))
+ {
+ perror("init_disk_names: fopen");
+ exit(1);
+ }
+
+ rewind(mtab);
+ fflush(mtab);
+
+ while(fgets(buf, sizeof(buf), mtab))
+ {
+ if(start = strstr(buf, "/dev/"))
+ {
+ if(start = strstr(start, "da"))
+ {
+ start -=1;
+ stop = strstr(start, " ");
+ memcpy(data->diskInfo.mdata[i].name, start, stop - start);
+ start = stop +1;
+ stop = strstr(start, " ");
+ memcpy(data->diskInfo.mdata[i].mountpoint, start, stop - start);
+ i++;
+ }
+ }
+ }
+
+ fclose(mtab);
+
+ return;
+}
+
+void get_diskinfo(PERF_DATA_BLOCK *data)
+{
+ int i;
+ DiskData *p;
+ struct statfs statfsbuf;
+ int status, num;
+ char buf[LARGE_BUF], *start;
+ FILE *diskstats;
+ long reads, writes, discard;
+
+ diskstats = fopen("/proc/diskstats", "r");
+ rewind(diskstats);
+ fflush(diskstats);
+ status = fread(buf, sizeof(char), LARGE_BUF, diskstats);
+ fclose(diskstats);
+
+ for(i = 0; i < data->diskInfo.numDisks; i++)
+ {
+ p = &(data->diskInfo.data[i]);
+ status = statfs(data->diskInfo.mdata[i].mountpoint, &statfsbuf);
+ p->freeMegs = (statfsbuf.f_bfree*statfsbuf.f_bsize)/1048576;
+ start = strstr(buf, data->diskInfo.mdata[i].name);
+ start += strlen(data->diskInfo.mdata[i].name) + 1;
+ num = sscanf(start, "%u %u %u %u",
+ &reads,
+ &discard,
+ &writes,
+ &discard);
+ p->writesPerSec = writes;
+ p->readsPerSec = reads;
+ fprintf(stderr, "%s:\t%u\t%u\n",
+ data->diskInfo.mdata[i].mountpoint,
+ reads, writes);
+ }
+ return;
+}
+void init_disk_data(PERF_DATA_BLOCK *data)
+{
+ init_diskdata_desc(data);
+
+ init_num_disks(data);
+
+ data->diskInfo.mdata = calloc(data->diskInfo.numDisks, sizeof(DiskMetaData));
+ if(!data->diskInfo.mdata)
+ {
+ fatal("init_disk_data: out of memory");
+ }
+
+ init_disk_names(data);
+
+ data->diskInfo.data = calloc(data->diskInfo.numDisks, sizeof(DiskData));
+ if(!data->diskInfo.data)
+ {
+ fatal("init_disk_data: out of memory");
+ }
+
+ get_diskinfo(data);
+
+ return;
+}
+
+void output_disk_desc(PERF_DATA_BLOCK *data, RuntimeSettings rt)
+{
+ output_perf_desc(data->diskInfo.diskObjDesc, rt);
+ output_perf_desc(data->diskInfo.freeMegs, rt);
+ output_perf_desc(data->diskInfo.writesPerSec, rt);
+ output_perf_desc(data->diskInfo.readsPerSec, rt);
+ output_num_instances(data->diskInfo.diskObjDesc, data->diskInfo.numDisks, rt);
+
+ return;
+}
+
+void output_diskinfo(PERF_DATA_BLOCK *data, RuntimeSettings rt, int tdb_flags)
+{
+ int i;
+
+ output_perf_counter(data->diskInfo.freeMegs,
+ data->diskInfo.data[0].freeMegs,
+ rt, tdb_flags);
+ output_perf_counter(data->diskInfo.writesPerSec,
+ (unsigned long long)data->diskInfo.data[0].writesPerSec,
+ rt, tdb_flags);
+ output_perf_counter(data->diskInfo.readsPerSec,
+ (unsigned long long)data->diskInfo.data[0].readsPerSec,
+ rt, tdb_flags);
+
+ for(i = 0; i < data->diskInfo.numDisks; i++)
+ {
+ output_perf_instance(data->diskInfo.diskObjDesc.index,
+ i,
+ (void *)&(data->diskInfo.data[i]),
+ sizeof(DiskData),
+ data->diskInfo.mdata[i].mountpoint,
+ rt, tdb_flags);
+ }
+
+ return;
+}
diff --git a/examples/perfcounter/perf_writer_mem.c b/examples/perfcounter/perf_writer_mem.c
new file mode 100644
index 0000000000..1a9a3ca959
--- /dev/null
+++ b/examples/perfcounter/perf_writer_mem.c
@@ -0,0 +1,125 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Performance Counter Daemon
+ *
+ * Copyright (C) Marcin Krzysztof Porwit 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "perf.h"
+
+void get_meminfo(PERF_DATA_BLOCK *data)
+{
+ int status;
+ struct sysinfo info;
+ status = sysinfo(&info);
+
+ data->memInfo.data->availPhysKb = (info.freeram * info.mem_unit)/1024;
+ data->memInfo.data->availSwapKb = (info.freeswap * info.mem_unit)/1024;
+ data->memInfo.data->totalPhysKb = (info.totalram * info.mem_unit)/1024;
+ data->memInfo.data->totalSwapKb = (info.totalswap * info.mem_unit)/1024;
+
+ /* Also get uptime since we have the structure */
+ data->PerfTime = (unsigned long)info.uptime;
+
+ return;
+}
+
+void init_memdata_desc(PERF_DATA_BLOCK *data)
+{
+ init_perf_counter(&(data->memInfo.memObjDesc),
+ &(data->memInfo.memObjDesc),
+ get_counter_id(data),
+ "Memory",
+ "The Memory performance object consists of counters that describe the behavior of physical and virtual memory on the computer.",
+ 0,
+ PERF_OBJECT);
+ init_perf_counter(&(data->memInfo.availPhysKb),
+ &(data->memInfo.memObjDesc),
+ get_counter_id(data),
+ "Available Physical Kilobytes",
+ "Available Physical Kilobytes is the number of free kilobytes in physical memory",
+ PERF_SIZE_DWORD | PERF_TYPE_NUMBER | PERF_NUMBER_DECIMAL | PERF_DISPLAY_NO_SUFFIX,
+ PERF_COUNTER);
+ init_perf_counter(&(data->memInfo.availSwapKb),
+ &(data->memInfo.memObjDesc),
+ get_counter_id(data),
+ "Available Swap Kilobytes",
+ "Available Swap Kilobytes is the number of free kilobytes in swap space",
+ PERF_SIZE_DWORD | PERF_TYPE_NUMBER | PERF_NUMBER_DECIMAL | PERF_DISPLAY_NO_SUFFIX,
+ PERF_COUNTER);
+ init_perf_counter(&(data->memInfo.totalPhysKb),
+ &(data->memInfo.memObjDesc),
+ get_counter_id(data),
+ "Total Physical Kilobytes",
+ "Total Physical Kilobytes is a base counter",
+ PERF_SIZE_DWORD | PERF_TYPE_NUMBER | PERF_NUMBER_DECIMAL | PERF_COUNTER_BASE | PERF_DISPLAY_NOSHOW,
+ PERF_COUNTER);
+ init_perf_counter(&(data->memInfo.totalSwapKb),
+ &(data->memInfo.memObjDesc),
+ get_counter_id(data),
+ "Total Swap Kilobytes",
+ "Total Swap Kilobytes is a base counter",
+ PERF_SIZE_DWORD | PERF_TYPE_NUMBER | PERF_NUMBER_DECIMAL | PERF_COUNTER_BASE | PERF_DISPLAY_NOSHOW,
+ PERF_COUNTER);
+
+ return;
+}
+
+void init_mem_data(PERF_DATA_BLOCK *data)
+{
+ data->memInfo.data = calloc(1, sizeof(*data->memInfo.data));
+ if(!data->memInfo.data)
+ {
+ perror("init_memdata: out of memory");
+ exit(1);
+ }
+
+ init_memdata_desc(data);
+
+ get_meminfo(data);
+
+ return;
+}
+
+void output_mem_desc(PERF_DATA_BLOCK *data, RuntimeSettings rt)
+{
+ output_perf_desc(data->memInfo.memObjDesc, rt);
+ output_perf_desc(data->memInfo.availPhysKb, rt);
+ output_perf_desc(data->memInfo.availSwapKb, rt);
+ output_perf_desc(data->memInfo.totalPhysKb, rt);
+ output_perf_desc(data->memInfo.totalSwapKb, rt);
+
+ return;
+}
+
+void output_meminfo(PERF_DATA_BLOCK *data, RuntimeSettings rt, int tdb_flags)
+{
+ output_perf_counter(data->memInfo.availPhysKb,
+ (unsigned long long)data->memInfo.data->availPhysKb,
+ rt, tdb_flags);
+ output_perf_counter(data->memInfo.availSwapKb,
+ (unsigned long long)data->memInfo.data->availSwapKb,
+ rt, tdb_flags);
+ output_perf_counter(data->memInfo.totalPhysKb,
+ (unsigned long long)data->memInfo.data->totalPhysKb,
+ rt, tdb_flags);
+ output_perf_counter(data->memInfo.totalSwapKb,
+ (unsigned long long)data->memInfo.data->totalSwapKb,
+ rt, tdb_flags);
+
+ return;
+}
diff --git a/examples/perfcounter/perf_writer_process.c b/examples/perfcounter/perf_writer_process.c
new file mode 100644
index 0000000000..75a23dae51
--- /dev/null
+++ b/examples/perfcounter/perf_writer_process.c
@@ -0,0 +1,86 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Performance Counter Daemon
+ *
+ * Copyright (C) Marcin Krzysztof Porwit 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "perf.h"
+
+void get_processinfo(PERF_DATA_BLOCK *data)
+{
+ int status;
+ struct sysinfo info;
+ status = sysinfo(&info);
+
+ data->processInfo.data->runningProcessCount = (unsigned int)info.procs;
+
+ return;
+}
+
+void init_processdata_desc(PERF_DATA_BLOCK *data)
+{
+ init_perf_counter(&(data->processInfo.processObjDesc),
+ &(data->processInfo.processObjDesc),
+ get_counter_id(data),
+ "Processes",
+ "%The Processes performance object displays aggregate information about processes on the machine.",
+ 0,
+ PERF_OBJECT);
+ init_perf_counter(&(data->processInfo.runningProcessCount),
+ &(data->processInfo.processObjDesc),
+ get_counter_id(data),
+ "Process Count",
+ "Process Count is the number of processes currently on the machine.",
+ PERF_SIZE_DWORD | PERF_TYPE_NUMBER | PERF_NUMBER_DECIMAL | PERF_DISPLAY_NO_SUFFIX,
+ PERF_COUNTER);
+
+ return;
+}
+
+void init_process_data(PERF_DATA_BLOCK *data)
+{
+ data->processInfo.data = calloc(1, sizeof(*data->processInfo.data));
+ if(!(data->processInfo.data))
+ {
+ perror("init_process_data: out of memory");
+ exit(1);
+ }
+
+ init_processdata_desc(data);
+
+ get_processinfo(data);
+
+ return;
+}
+
+void output_processinfo(PERF_DATA_BLOCK *data, RuntimeSettings rt, int tdb_flags)
+{
+ output_perf_counter(data->processInfo.runningProcessCount,
+ (unsigned long long)data->processInfo.data->runningProcessCount,
+ rt, tdb_flags);
+
+ return;
+}
+
+void output_process_desc(PERF_DATA_BLOCK *data, RuntimeSettings rt)
+{
+ output_perf_desc(data->processInfo.processObjDesc, rt);
+ output_perf_desc(data->processInfo.runningProcessCount, rt);
+
+ return;
+}
diff --git a/examples/perfcounter/perf_writer_util.c b/examples/perfcounter/perf_writer_util.c
new file mode 100644
index 0000000000..bb6422bac2
--- /dev/null
+++ b/examples/perfcounter/perf_writer_util.c
@@ -0,0 +1,244 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Performance Counter Daemon
+ *
+ * Copyright (C) Marcin Krzysztof Porwit 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "perf.h"
+
+extern sig_atomic_t keep_running;
+
+void fatal(char *msg)
+{
+ perror(msg);
+ exit(1);
+}
+
+void add_key_raw(TDB_CONTEXT *db, char *keystring, void *databuf, size_t datasize, int flags)
+{
+ TDB_DATA key, data;
+
+ key.dptr = keystring;
+ key.dsize = strlen(keystring);
+ data.dptr = databuf;
+ data.dsize = datasize;
+ fprintf(stderr, "doing insert of [%x] with key [%s] into [%s]\n",
+ data.dptr,
+ keystring,
+ db->name);
+
+ tdb_store(db, key, data, flags);
+}
+
+void add_key(TDB_CONTEXT *db, char *keystring, char *datastring, int flags)
+{
+ TDB_DATA key, data;
+
+ key.dptr = keystring;
+ key.dsize = strlen(keystring);
+ data.dptr = datastring;
+ data.dsize = strlen(datastring);
+ /* fprintf(stderr, "doing insert of [%s] with key [%s] into [%s]\n",
+ data.dptr,
+ keystring,
+ db->name);*/
+
+ tdb_store(db, key, data, flags);
+}
+
+void make_key(char *buf, int buflen, int key_part1, char *key_part2)
+{
+ memset(buf, 0, buflen);
+ if(key_part2 != NULL)
+ sprintf(buf, "%d%s", key_part1, key_part2);
+ else
+ sprintf(buf, "%d", key_part1);
+
+ return;
+}
+
+void usage(char *progname)
+{
+ fprintf(stderr, "Usage: %s [-d] [-f <file_path>].\n", progname);
+ fprintf(stderr, "\t-d: run as a daemon.\n");
+ fprintf(stderr, "\t-f <file_path>: path where the TDB files reside.\n");
+ fprintf(stderr, "\t\tDEFAULT is /tmp/counters\n");
+ exit(1);
+}
+
+void parse_flags(RuntimeSettings *rt, int argc, char **argv)
+{
+ int flag;
+
+ while((flag = getopt(argc, argv, "df:")) != -1)
+ {
+ switch(flag)
+ {
+ case 'd':
+ {
+ rt->dflag = TRUE;
+ break;
+ }
+ case 'f':
+ {
+ memcpy(rt->dbDir, optarg, strlen(optarg));
+ break;
+ }
+ default:
+ {
+ usage(argv[0]);
+ }
+ }
+ }
+
+ return;
+}
+
+void setup_file_paths(RuntimeSettings *rt)
+{
+ int status;
+
+ if(strlen(rt->dbDir) == 0)
+ {
+ /* No file path was passed in, use default */
+ sprintf(rt->dbDir, "/tmp/counters");
+ }
+
+ sprintf(rt->nameFile, "%s/names.tdb", rt->dbDir);
+ sprintf(rt->counterFile, "%s/data.tdb", rt->dbDir);
+
+ mkdir(rt->dbDir, O_RDWR);
+ rt->cnames = tdb_open(rt->nameFile, 0, TDB_CLEAR_IF_FIRST, O_RDWR | O_CREAT, 0644);
+ rt->cdata = tdb_open(rt->counterFile, 0, TDB_CLEAR_IF_FIRST, O_RDWR | O_CREAT, 0644);
+
+ if(rt->cnames == NULL || rt->cdata == NULL)
+ {
+ perror("setup_file_paths");
+ exit(1);
+ }
+
+ return;
+}
+
+void sigterm_handler()
+{
+ keep_running = FALSE;
+ return;
+}
+
+void daemonize(RuntimeSettings *rt)
+{
+ pid_t pid;
+ int i;
+ int fd;
+
+ /* Check if we're already a daemon */
+ if(getppid() == 1)
+ return;
+ pid = fork();
+ if(pid < 0)
+ /* can't fork */
+ exit(1);
+ else if(pid > 0)
+ {
+ /* we're the parent */
+ tdb_close(rt->cnames);
+ tdb_close(rt->cdata);
+ exit(0);
+ }
+
+ /* get a new session */
+ if(setsid() == -1)
+ exit(2);
+
+ /* Change CWD */
+ chdir("/");
+
+ /* close file descriptors */
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ /* And reopen them as safe defaults */
+ fd = open("/dev/null", O_RDONLY);
+ if(fd != 0)
+ {
+ dup2(fd, 0);
+ close(fd);
+ }
+ fd = open("/dev/null", O_WRONLY);
+ if(fd != 1)
+ {
+ dup2(fd, 1);
+ close(fd);
+ }
+ fd = open("/dev/null", O_WRONLY);
+ if(fd != 2)
+ {
+ dup2(fd, 2);
+ close(fd);
+ }
+
+ /* handle signals */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGTERM, sigterm_handler);
+
+ return;
+}
+
+int get_counter_id(PERF_DATA_BLOCK *data)
+{
+ data->counter_id += 2;
+ data->num_counters++;
+
+ return data->counter_id;
+}
+
+void init_perf_counter(PerfCounter *counter,
+ PerfCounter *parent,
+ unsigned int index,
+ char *name,
+ char *help,
+ int counter_type,
+ int record_type)
+{
+ counter->index = index;
+ memcpy(counter->name, name, strlen(name));
+ memcpy(counter->help, help, strlen(help));
+ counter->counter_type = counter_type;
+ counter->record_type = record_type;
+
+ switch(record_type)
+ {
+ case PERF_OBJECT:
+ sprintf(counter->relationships, "p");
+ break;
+ case PERF_COUNTER:
+ sprintf(counter->relationships, "c[%d]", parent->index);
+ break;
+ case PERF_INSTANCE:
+ sprintf(counter->relationships, "i[%d]", parent->index);
+ break;
+ default:
+ perror("init_perf_counter: unknown record type");
+ exit(1);
+ }
+
+ return;
+}
diff --git a/examples/perfcounter/perfcountd.init b/examples/perfcounter/perfcountd.init
new file mode 100755
index 0000000000..bb4148e52f
--- /dev/null
+++ b/examples/perfcounter/perfcountd.init
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (C) Gerald Carter 2005
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+####################################################################
+
+## This file should have uid root, gid sys and chmod 744
+
+PATH=/bin:/usr/bin:/sbin:/usr/sbin
+
+killproc()
+{
+ pid=`ps aux | grep $1 | egrep -v '(grep|perfcountd)' | awk '{print $2}'`
+ if [ "$pid" != "" ]; then
+ kill $pid
+ fi
+}
+
+# Start/stop processes
+
+case "$1"
+in
+start)
+ /opt/samba/bin/perfcount -d -f /var/lib/samba/perfmon 2> /dev/null
+ if [ $? -ne 0 ]; then
+ echo "Failed!"
+ exit 1
+ fi
+ echo "done!"
+ ;;
+stop)
+ killproc perfcount
+ ;;
+
+status)
+ pid=`ps aux | grep perfcount | egrep -v '(grep|perfcountd)' | awk '{print $2}'`
+ if [ "$pid" == "" ]; then
+ echo "Dead!"
+ exit 2;
+ fi
+ echo "OK!"
+ ;;
+restart)
+ $0 stop && $0 start
+ ;;
+
+*)
+ echo "Usage: $0 { start|stop|restart|status }"
+ ;;
+esac
+
+
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index d2cbf380da..d2d739fa72 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -121,7 +121,6 @@ typedef struct
char *szConfigFile;
char *szSMBPasswdFile;
char *szPrivateDir;
- char *szCountersDir;
char **szPassdbBackend;
char **szPreloadModules;
char *szPasswordServer;
@@ -848,7 +847,6 @@ static struct parm_struct parm_table[] = {
{"password server", P_STRING, P_GLOBAL, &Globals.szPasswordServer, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
{"smb passwd file", P_STRING, P_GLOBAL, &Globals.szSMBPasswdFile, NULL, NULL, FLAG_ADVANCED},
{"private dir", P_STRING, P_GLOBAL, &Globals.szPrivateDir, NULL, NULL, FLAG_ADVANCED},
- {"counters dir", P_STRING, P_GLOBAL, &Globals.szCountersDir, NULL, NULL, FLAG_ADVANCED},
{"passdb backend", P_LIST, P_GLOBAL, &Globals.szPassdbBackend, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
{"algorithmic rid base", P_INTEGER, P_GLOBAL, &Globals.AlgorithmicRidBase, NULL, NULL, FLAG_ADVANCED},
{"root directory", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL, NULL, FLAG_ADVANCED},
@@ -1711,7 +1709,6 @@ FN_GLOBAL_STRING(lp_logfile, &Globals.szLogFile)
FN_GLOBAL_STRING(lp_configfile, &Globals.szConfigFile)
FN_GLOBAL_STRING(lp_smb_passwd_file, &Globals.szSMBPasswdFile)
FN_GLOBAL_STRING(lp_private_dir, &Globals.szPrivateDir)
-FN_GLOBAL_STRING(lp_counters_dir, &Globals.szCountersDir)
FN_GLOBAL_STRING(lp_serverstring, &Globals.szServerString)
FN_GLOBAL_INTEGER(lp_printcap_cache_time, &Globals.PrintcapCacheTime)
FN_GLOBAL_STRING(lp_enumports_cmd, &Globals.szEnumPortsCommand)
diff --git a/source3/registry/reg_frontend.c b/source3/registry/reg_frontend.c
index b0e713a882..d5a9d45e8c 100644
--- a/source3/registry/reg_frontend.c
+++ b/source3/registry/reg_frontend.c
@@ -131,6 +131,7 @@ BOOL init_registry( void )
svcctl_init_keys();
eventlog_init_keys();
+ perfcount_init_keys();
/* close and let each smbd open up as necessary */
diff --git a/source3/registry/reg_perfcount.c b/source3/registry/reg_perfcount.c
index fe8b355b95..a31154fc33 100644
--- a/source3/registry/reg_perfcount.c
+++ b/source3/registry/reg_perfcount.c
@@ -1,3 +1,25 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ *
+ * Copyright (C) Marcin Krzysztof Porwit 2005,
+ * Copyright (C) Gerald (Jerry) Carter 2005.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
#include "includes.h"
#undef DBGC_CLASS
@@ -5,21 +27,54 @@
#define PERFCOUNT_MAX_LEN 256
+#define PERFCOUNTDIR "perfmon"
+#define NAMES_DB "names.tdb"
+#define DATA_DB "data.tdb"
+
+/*********************************************************************
+*********************************************************************/
+
+static char* counters_directory( const char *dbname )
+{
+ static pstring fname;
+ fstring path;
+
+ if ( !dbname )
+ return NULL;
+
+ fstr_sprintf( path, "%s/%s", PERFCOUNTDIR, dbname );
+
+ pstrcpy( fname, lock_path( path ) );
+
+ return fname;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+void perfcount_init_keys( void )
+{
+ char *p = lock_path(PERFCOUNTDIR);
+
+ /* no registry keys; just create the perfmon directory */
+
+ if ( !directory_exist( p, NULL ) )
+ mkdir( p, 0755 );
+
+ return;
+}
+
+/*********************************************************************
+*********************************************************************/
+
uint32 reg_perfcount_get_base_index(void)
{
- pstring fname;
+ const char *fname = counters_directory( NAMES_DB );
TDB_CONTEXT *names;
TDB_DATA kbuf, dbuf;
char key[] = "1";
uint32 retval = 0;
char buf[PERFCOUNT_MAX_LEN];
- const char *counter_dir = lp_counters_dir();
-
-
- if ( !*counter_dir )
- return 0;
-
- pstr_sprintf( fname, "%s/names.tdb", counter_dir );
names = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444);
@@ -62,6 +117,9 @@ uint32 reg_perfcount_get_base_index(void)
return 0;
}
+/*********************************************************************
+*********************************************************************/
+
uint32 reg_perfcount_get_last_counter(uint32 base_index)
{
uint32 retval;
@@ -74,6 +132,9 @@ uint32 reg_perfcount_get_last_counter(uint32 base_index)
return retval;
}
+/*********************************************************************
+*********************************************************************/
+
uint32 reg_perfcount_get_last_help(uint32 last_counter)
{
uint32 retval;
@@ -86,6 +147,10 @@ uint32 reg_perfcount_get_last_help(uint32 last_counter)
return retval;
}
+
+/*********************************************************************
+*********************************************************************/
+
static uint32 _reg_perfcount_multi_sz_from_tdb(TDB_CONTEXT *tdb,
int keyval,
char **retbuf,
@@ -145,20 +210,20 @@ static uint32 _reg_perfcount_multi_sz_from_tdb(TDB_CONTEXT *tdb,
return buffer_size;
}
+/*********************************************************************
+*********************************************************************/
+
uint32 reg_perfcount_get_counter_help(uint32 base_index, char **retbuf)
{
char *buf1 = NULL, *buf2 = NULL;
uint32 buffer_size = 0;
TDB_CONTEXT *names;
- pstring fname;
+ const char *fname = counters_directory( NAMES_DB );
int i;
if(base_index == 0)
return 0;
- pstrcpy(fname, lp_counters_dir());
- pstrcat(fname, "/names.tdb");
-
names = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444);
if(names == NULL)
@@ -193,20 +258,20 @@ uint32 reg_perfcount_get_counter_help(uint32 base_index, char **retbuf)
return buffer_size;
}
+/*********************************************************************
+*********************************************************************/
+
uint32 reg_perfcount_get_counter_names(uint32 base_index, char **retbuf)
{
char *buf1 = NULL, *buf2 = NULL;
uint32 buffer_size = 0;
TDB_CONTEXT *names;
- pstring fname;
+ const char *fname = counters_directory( NAMES_DB );
int i;
if(base_index == 0)
return 0;
- pstrcpy(fname, lp_counters_dir());
- pstrcat(fname, "/names.tdb");
-
names = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444);
if(names == NULL)
@@ -243,6 +308,9 @@ uint32 reg_perfcount_get_counter_names(uint32 base_index, char **retbuf)
return buffer_size;
}
+/*********************************************************************
+*********************************************************************/
+
static void _reg_perfcount_make_key(TDB_DATA *key,
char *buf,
int buflen,
@@ -261,6 +329,9 @@ static void _reg_perfcount_make_key(TDB_DATA *key,
return;
}
+/*********************************************************************
+*********************************************************************/
+
static BOOL _reg_perfcount_isparent(TDB_DATA data)
{
if(data.dsize > 0)
@@ -273,6 +344,9 @@ static BOOL _reg_perfcount_isparent(TDB_DATA data)
return False;
}
+/*********************************************************************
+*********************************************************************/
+
static BOOL _reg_perfcount_ischild(TDB_DATA data)
{
if(data.dsize > 0)
@@ -285,6 +359,9 @@ static BOOL _reg_perfcount_ischild(TDB_DATA data)
return False;
}
+/*********************************************************************
+*********************************************************************/
+
static uint32 _reg_perfcount_get_numinst(int objInd, TDB_CONTEXT *names)
{
TDB_DATA key, data;
@@ -301,6 +378,9 @@ static uint32 _reg_perfcount_get_numinst(int objInd, TDB_CONTEXT *names)
return (uint32)atoi(buf);
}
+/*********************************************************************
+*********************************************************************/
+
static BOOL _reg_perfcount_add_object(PERF_DATA_BLOCK *block,
prs_struct *ps,
int num,
@@ -341,15 +421,14 @@ static BOOL _reg_perfcount_add_object(PERF_DATA_BLOCK *block,
return True;
}
+/*********************************************************************
+*********************************************************************/
+
BOOL _reg_perfcount_get_counter_data(TDB_DATA key, TDB_DATA *data)
{
TDB_CONTEXT *counters;
-
- pstring fname;
+ const char *fname = counters_directory( DATA_DB );
- pstrcpy(fname, lp_counters_dir());
- pstrcat(fname, "/data.tdb");
-
counters = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444);
if(counters == NULL)
@@ -365,6 +444,9 @@ BOOL _reg_perfcount_get_counter_data(TDB_DATA key, TDB_DATA *data)
return True;
}
+/*********************************************************************
+*********************************************************************/
+
static uint32 _reg_perfcount_get_size_field(uint32 CounterType)
{
uint32 retval;
@@ -379,7 +461,10 @@ static uint32 _reg_perfcount_get_size_field(uint32 CounterType)
return retval;
}
-static uint32 _reg_perfcount_compute_scale(long long int data)
+/*********************************************************************
+*********************************************************************/
+
+static uint32 _reg_perfcount_compute_scale(SMB_BIG_INT data)
{
int scale = 0;
if(data == 0)
@@ -398,6 +483,9 @@ static uint32 _reg_perfcount_compute_scale(long long int data)
return (uint32)scale;
}
+/*********************************************************************
+*********************************************************************/
+
static BOOL _reg_perfcount_get_counter_info(PERF_DATA_BLOCK *block,
prs_struct *ps,
int CounterIndex,
@@ -408,7 +496,7 @@ static BOOL _reg_perfcount_get_counter_info(PERF_DATA_BLOCK *block,
char buf[PERFCOUNT_MAX_LEN];
size_t dsize, padding;
long int data32, dbuf[2];
- long long int data64;
+ SMB_BIG_INT data64;
uint32 counter_size;
obj->counters[obj->NumCounters].DefaultScale = 0;
@@ -447,7 +535,7 @@ static BOOL _reg_perfcount_get_counter_info(PERF_DATA_BLOCK *block,
memcpy(buf, data.dptr, data.dsize);
data32 = strtol(buf, NULL, 0);
if((obj->counters[obj->NumCounters].CounterType & 0x00000F00) == PERF_TYPE_NUMBER)
- obj->counters[obj->NumCounters].DefaultScale = _reg_perfcount_compute_scale((long long int)data32);
+ obj->counters[obj->NumCounters].DefaultScale = _reg_perfcount_compute_scale((SMB_BIG_INT)data32);
else
obj->counters[obj->NumCounters].DefaultScale = 0;
dbuf[0] = data32;
@@ -458,7 +546,7 @@ static BOOL _reg_perfcount_get_counter_info(PERF_DATA_BLOCK *block,
dsize = sizeof(data64);
memset(buf, 0, PERFCOUNT_MAX_LEN);
memcpy(buf, data.dptr, data.dsize);
- data64 = strtoll(buf, NULL, 0);
+ data64 = atof(buf);
if((obj->counters[obj->NumCounters].CounterType & 0x00000F00) == PERF_TYPE_NUMBER)
obj->counters[obj->NumCounters].DefaultScale = _reg_perfcount_compute_scale(data64);
else
@@ -504,6 +592,9 @@ static BOOL _reg_perfcount_get_counter_info(PERF_DATA_BLOCK *block,
return True;
}
+/*********************************************************************
+*********************************************************************/
+
PERF_OBJECT_TYPE *_reg_perfcount_find_obj(PERF_DATA_BLOCK *block, int objind)
{
int i;
@@ -521,6 +612,9 @@ PERF_OBJECT_TYPE *_reg_perfcount_find_obj(PERF_DATA_BLOCK *block, int objind)
return obj;
}
+/*********************************************************************
+*********************************************************************/
+
static BOOL _reg_perfcount_add_counter(PERF_DATA_BLOCK *block,
prs_struct *ps,
int num,
@@ -582,6 +676,9 @@ static BOOL _reg_perfcount_add_counter(PERF_DATA_BLOCK *block,
return True;
}
+/*********************************************************************
+*********************************************************************/
+
BOOL _reg_perfcount_get_instance_info(PERF_INSTANCE_DEFINITION *inst,
prs_struct *ps,
int instId,
@@ -662,6 +759,9 @@ BOOL _reg_perfcount_get_instance_info(PERF_INSTANCE_DEFINITION *inst,
return True;
}
+/*********************************************************************
+*********************************************************************/
+
BOOL _reg_perfcount_add_instance(PERF_OBJECT_TYPE *obj,
prs_struct *ps,
int instInd,
@@ -689,6 +789,9 @@ BOOL _reg_perfcount_add_instance(PERF_OBJECT_TYPE *obj,
return True;
}
+/*********************************************************************
+*********************************************************************/
+
static int _reg_perfcount_assemble_global(PERF_DATA_BLOCK *block,
prs_struct *ps,
int base_index,
@@ -728,7 +831,10 @@ static int _reg_perfcount_assemble_global(PERF_DATA_BLOCK *block,
return retval;
}
-static BOOL _reg_perfcount_get_64(unsigned long long *retval,
+/*********************************************************************
+*********************************************************************/
+
+static BOOL _reg_perfcount_get_64(SMB_BIG_UINT *retval,
TDB_CONTEXT *tdb,
int key_part1,
const char *key_part2)
@@ -749,23 +855,21 @@ static BOOL _reg_perfcount_get_64(unsigned long long *retval,
memcpy(buf, data.dptr, data.dsize);
free(data.dptr);
- *retval = strtoll(buf, NULL, 0);
+ *retval = atof(buf);
return True;
}
+/*********************************************************************
+*********************************************************************/
+
static BOOL _reg_perfcount_init_data_block_perf(PERF_DATA_BLOCK *block,
TDB_CONTEXT *names)
{
- unsigned long long PerfFreq, PerfTime, PerfTime100nSec;
+ SMB_BIG_UINT PerfFreq, PerfTime, PerfTime100nSec;
TDB_CONTEXT *counters;
- BOOL status;
- pstring fname;
-
- status = False;
-
- pstrcpy(fname, lp_counters_dir());
- pstrcat(fname, "/data.tdb");
+ BOOL status = False;
+ const char *fname = counters_directory( DATA_DB );
counters = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444);
@@ -803,6 +907,9 @@ static BOOL _reg_perfcount_init_data_block_perf(PERF_DATA_BLOCK *block,
return True;
}
+/*********************************************************************
+*********************************************************************/
+
static void _reg_perfcount_init_data_block(PERF_DATA_BLOCK *block, prs_struct *ps, TDB_CONTEXT *names)
{
wpstring temp;
@@ -839,6 +946,9 @@ static void _reg_perfcount_init_data_block(PERF_DATA_BLOCK *block, prs_struct *p
return;
}
+/*********************************************************************
+*********************************************************************/
+
static uint32 _reg_perfcount_perf_data_block_fixup(PERF_DATA_BLOCK *block, prs_struct *ps)
{
int obj, cnt, inst, pad, i;
@@ -923,20 +1033,20 @@ static uint32 _reg_perfcount_perf_data_block_fixup(PERF_DATA_BLOCK *block, prs_s
return block->TotalByteLength;
}
-
+
+/*********************************************************************
+*********************************************************************/
+
uint32 reg_perfcount_get_perf_data_block(uint32 base_index,
prs_struct *ps,
PERF_DATA_BLOCK *block,
char *object_ids)
{
uint32 buffer_size = 0, last_counter;
- pstring fname;
+ const char *fname = counters_directory( NAMES_DB );
TDB_CONTEXT *names;
int retval;
-
- pstrcpy(fname, lp_counters_dir());
- pstrcat(fname, "/names.tdb");
-
+
names = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444);
if(names == NULL)
@@ -966,6 +1076,9 @@ uint32 reg_perfcount_get_perf_data_block(uint32 base_index,
return buffer_size + block->HeaderLength;
}
+/*********************************************************************
+*********************************************************************/
+
static BOOL _reg_perfcount_marshall_perf_data_block(prs_struct *ps, PERF_DATA_BLOCK block, int depth)
{
int i;
@@ -1017,6 +1130,9 @@ static BOOL _reg_perfcount_marshall_perf_data_block(prs_struct *ps, PERF_DATA_BL
return True;
}
+/*********************************************************************
+*********************************************************************/
+
static BOOL _reg_perfcount_marshall_perf_counters(prs_struct *ps,
PERF_OBJECT_TYPE object,
int depth)
@@ -1058,6 +1174,9 @@ static BOOL _reg_perfcount_marshall_perf_counters(prs_struct *ps,
return True;
}
+/*********************************************************************
+*********************************************************************/
+
static BOOL _reg_perfcount_marshall_perf_counter_data(prs_struct *ps,
PERF_COUNTER_BLOCK counter_data,
int depth)
@@ -1078,6 +1197,9 @@ static BOOL _reg_perfcount_marshall_perf_counter_data(prs_struct *ps,
return True;
}
+/*********************************************************************
+*********************************************************************/
+
static BOOL _reg_perfcount_marshall_perf_instances(prs_struct *ps,
PERF_OBJECT_TYPE object,
int depth)
@@ -1116,6 +1238,9 @@ static BOOL _reg_perfcount_marshall_perf_instances(prs_struct *ps,
return True;
}
+/*********************************************************************
+*********************************************************************/
+
static BOOL _reg_perfcount_marshall_perf_objects(prs_struct *ps, PERF_DATA_BLOCK block, int depth)
{
int obj;
@@ -1183,6 +1308,9 @@ static BOOL _reg_perfcount_marshall_perf_objects(prs_struct *ps, PERF_DATA_BLOCK
return True;
}
+/*********************************************************************
+*********************************************************************/
+
static BOOL _reg_perfcount_marshall_hkpd(prs_struct *ps, PERF_DATA_BLOCK block)
{
int depth = 0;
@@ -1193,6 +1321,10 @@ static BOOL _reg_perfcount_marshall_hkpd(prs_struct *ps, PERF_DATA_BLOCK block)
}
return False;
}
+
+/*********************************************************************
+*********************************************************************/
+
WERROR reg_perfcount_get_hkpd(prs_struct *ps, uint32 max_buf_size, uint32 *outbuf_len, char *object_ids)
{
/*
diff --git a/source3/services/services_db.c b/source3/services/services_db.c
index 477bf4e269..5c87225f57 100644
--- a/source3/services/services_db.c
+++ b/source3/services/services_db.c
@@ -71,6 +71,7 @@ struct service_display_info common_unix_svcs[] = {
{ "apache", NULL, "HTTP Server", NULL },
{ "autofs", NULL, "Automounter", NULL },
{ "squid", NULL, "Web Cache Proxy ", NULL },
+ { "perfcountd", NULL, "Performance Monitoring Daemon", NULL },
{ NULL, NULL, NULL, NULL }
};
diff --git a/source3/utils/net_rpc_registry.c b/source3/utils/net_rpc_registry.c
index 7397c88d95..289fb59fea 100644
--- a/source3/utils/net_rpc_registry.c
+++ b/source3/utils/net_rpc_registry.c
@@ -117,11 +117,14 @@ static NTSTATUS rpc_registry_enumerate_internal(const DOM_SID *domain_sid,
return werror_to_ntstatus(result);
}
- result = rpccli_reg_open_entry(pipe_hnd, mem_ctx, &pol_hive, subpath, MAXIMUM_ALLOWED_ACCESS, &pol_key );
- if ( !W_ERROR_IS_OK(result) ) {
- d_printf("Unable to open [%s]\n", argv[0]);
- return werror_to_ntstatus(result);
+ if ( strlen( subpath ) != 0 ) {
+ result = rpccli_reg_open_entry(pipe_hnd, mem_ctx, &pol_hive, subpath, MAXIMUM_ALLOWED_ACCESS, &pol_key );
+ if ( !W_ERROR_IS_OK(result) ) {
+ d_printf("Unable to open [%s]\n", argv[0]);
+ return werror_to_ntstatus(result);
+ }
}
+ memcpy( &pol_key, &pol_hive, sizeof(POLICY_HND) );
/* get the subkeys */
@@ -183,7 +186,8 @@ static NTSTATUS rpc_registry_enumerate_internal(const DOM_SID *domain_sid,
out:
/* cleanup */
- rpccli_reg_close(pipe_hnd, mem_ctx, &pol_key );
+ if ( strlen( subpath ) != 0 )
+ rpccli_reg_close(pipe_hnd, mem_ctx, &pol_key );
rpccli_reg_close(pipe_hnd, mem_ctx, &pol_hive );
return werror_to_ntstatus(result);