Permalink
Browse files

PHP support from http://bugzilla.ganglia.info/cgi-bin/bugzilla/show_b…

…ug.cgi?id=282 set to be disabled by default.
  • Loading branch information...
1 parent 3c3cda4 commit 4aa9dc436f1999d610b6f2993adf1bd8ed2ffd13 @jbuchbinder jbuchbinder committed May 28, 2012
View
2 bootstrap
@@ -20,7 +20,7 @@ cd ..
echo "Create distribution timestamp"
touch Makefile.am
echo "Running aclocal" &&
-aclocal &&
+aclocal -I m4 &&
echo "Running autoheader" &&
autoheader &&
echo "Creating build" &&
View
82 configure.in
@@ -167,6 +167,10 @@ AC_ARG_WITH( python,
[ --with-python=PATH Specify prefix for python or full path to interpreter],
[if test x"$withval" != xno; then enable_python="yes"; PYTHON_BIN="$withval"; fi])
+AC_ARG_WITH( php,
+[ --with-php=PATH Specify prefix for php or full path to php-config],
+[if test x"$withval" != xno; then enable_php="yes"; PHP_BIN="$withval"; fi])
+
AC_ARG_WITH( librrd,
[ --with-librrd=DIR Specify location for librrd],
[if test x"$withval" != xno; then librrd="yes"; librrdpath="$withval"; fi])
@@ -238,6 +242,10 @@ AC_ARG_ENABLE( python,
[ --disable-python exclude mod_python and support for metric modules written in python],
[ if test x"$enableval" != xyes; then enable_python="no"; fi ], [ enable_python="yes" ] )
+AC_ARG_ENABLE( php,
+[ --enable-php include mod_php and support for metric modules written in php],
+[ if test x"$enableval" != xno; then enable_php="yes"; fi ], [ enable_php="no" ] )
+
AC_ARG_ENABLE( status,
[ --enable-status compile and install modgstatus to enable gmond status metric module support],
[ enable_status="yes"
@@ -337,6 +345,77 @@ AC_MSG_CHECKING(Python support)
AC_MSG_RESULT($enable_python)
AM_CONDITIONAL(BUILD_PYTHON, test x"$enable_python" = xyes)
+if test x"$enable_php" = xyes; then
+ echo
+ echo "Checking for php"
+
+ # check for php-config executable
+ if test -e "$PHP_BIN"; then
+ AC_MSG_CHECKING([for php-config])
+ if test -d "$PHP_BIN"; then
+ if test -x "$PHP_BIN/bin/php-config"; then
+ PHP_CONFIG_BIN="$PHP_BIN/bin/php-config"
+ elif test -x "$PHP_BIN/php-config"; then
+ PHP_CONFIG_BIN="$PHP_BIN/php-config"
+ fi
+ elif test -x "$PHP_BIN"; then
+ PHP_CONFIG_BIN=$PHP_BIN
+ fi
+ if test -z "$PHP_CONFIG_BIN"; then
+ AC_MSG_RESULT([not found using $PHP_BIN])
+ else
+ AC_MSG_RESULT([$PHP_CONFIG_BIN])
+ fi
+ fi
+ if test -z "$PHP_CONFIG_BIN"; then
+ AC_PATH_PROG([PHP_CONFIG_BIN], [php-config])
+ fi
+
+ if test -n "$PHP_CONFIG_BIN"; then
+ # find out php version
+ PHP_FULL_VERSION=`$PHP_CONFIG_BIN --version`
+ AC_CACHE_CHECK([PHP version >= 5.3], [php_cv_version_5],
+ [AX_COMPARE_VERSION([$PHP_FULL_VERSION], [ge], [5.3],
+ [php_cv_version_5="yes"],
+ [php_cv_version_5="no"])])
+ if test "$php_cv_version_5" = "yes"; then
+ PHP_VERSION=5
+ else
+ AC_MSG_ERROR([PHP version >= 5.3 required])
+ fi
+ AC_SUBST(PHP_VERSION)
+
+ AC_MSG_CHECKING(PHP Include dir)
+ PHP_INCLUDE_DIR=`$PHP_CONFIG_BIN --include-dir`
+ AC_MSG_RESULT($PHP_INCLUDE_DIR)
+
+ if test -f "$PHP_INCLUDE_DIR/sapi/embed/php_embed.h"; then
+ PHP_INCLUDES=`$PHP_CONFIG_BIN --includes`
+ CPPFLAGS+=$PHP_INCLUDES
+ LDFLAGS+=" -L`$PHP_CONFIG_BIN --prefix`/lib"
+ else
+ PHP_INCLUDES=""
+ fi
+ AC_CHECK_HEADER([sapi/embed/php_embed.h], [],
+ [
+ AC_MSG_ERROR([your system is missing the PHP SAPI headers.])
+ enable_php="no"
+ ],
+[#ifdef _PHP_EMBED_H_
+# include <sapi/embed/php_embed.h>
+#endif
+])
+ AC_SUBST(PHP_INCLUDES)
+ else
+ AC_MSG_ERROR("php-config not found")
+ enable_php="no"
+ fi
+fi
+
+AC_MSG_CHECKING(PHP support)
+AC_MSG_RESULT($enable_php)
+AM_CONDITIONAL(BUILD_PHP, test x"$enable_php" = xyes)
+
LIB_SUFFIX=lib
case $host in
x86_64*linux*)
@@ -802,9 +881,12 @@ AC_OUTPUT(Makefile
gmond/modules/disk/Makefile
gmond/modules/memory/Makefile
gmond/modules/network/Makefile
+ gmond/modules/php/Makefile
gmond/modules/python/Makefile
gmond/modules/status/Makefile
gmond/modules/system/Makefile
+ gmond/php_modules/Makefile
+ gmond/php_modules/example/Makefile
gmond/python_modules/Makefile
gmond/python_modules/apache_status/Makefile
gmond/python_modules/db/Makefile
View
6 gmond/Makefile.am
@@ -1,7 +1,7 @@
include $(top_srcdir)/ganglia.inc
-SUBDIRS = modules python_modules
+SUBDIRS = modules python_modules php_modules
EXTRA_DIST = gmond.aix.init gmond.solaris.init.in gmond.init gmond.init.SuSE gmond.conf.5 gmond.conf.html conf.pod cmdline.sh
if STATIC_BUILD
@@ -20,6 +20,10 @@ if BUILD_PYTHON
GLDADD += modules/python/mod_python.lo
GLDFLAGS += -lpython@PYTHON_VERSION@
endif
+if BUILD_PHP
+GLDADD += modules/php/mod_php.lo
+GLDFLAGS += -lphp@PHP_VERSION@
+endif
else
GCFLAGS = -D_LARGEFILE64_SOURCE
GLDADD =
View
10 gmond/modules/Makefile.am
@@ -2,15 +2,19 @@ if BUILD_PYTHON
PYTHON_SUBDIR = python
endif
+if BUILD_PHP
+PHP_SUBDIR = php
+endif
+
if BUILD_STATUS
STATUS_SUBDIR = status
endif
-DIST_SUBDIRS = example cpu disk memory network system status python
+DIST_SUBDIRS = example cpu disk memory network system status python php
if STATIC_BUILD
-SUBDIRS = cpu disk memory network system $(STATUS_SUBDIR) $(PYTHON_SUBDIR)
+SUBDIRS = cpu disk memory network system $(STATUS_SUBDIR) $(PYTHON_SUBDIR) $(PHP_SUBDIR)
else
-SUBDIRS = example cpu disk memory network system $(STATUS_SUBDIR) $(PYTHON_SUBDIR)
+SUBDIRS = example cpu disk memory network system $(STATUS_SUBDIR) $(PYTHON_SUBDIR) $(PHP_SUBDIR)
install: install-recursive
@rm -rf $(DESTDIR)$(pkglibdir)/*.a
View
27 gmond/modules/conf.d/modphp.conf.in
@@ -0,0 +1,27 @@
+/*
+ param php_modules_path
+ path to the directory where mod_php
+ should look for php metric modules
+
+ param php_ini_path
+ path to the php.ini used by the php engine
+
+ the "phpconf" files in the include directory below
+ will be scanned for configurations for those modules
+*/
+modules {
+ module {
+ name = "php_module"
+ path = "modphp.so"
+ param php_modules_path {
+ value = "@moduledir@/php_modules"
+ }
+ /*
+ param php_ini_path {
+ value = "/etc/php.ini"
+ }
+ */
+ }
+}
+
+include ("@sysconfdir@/conf.d/*.phpconf")
View
30 gmond/modules/php/Makefile.am
@@ -0,0 +1,30 @@
+
+include $(top_srcdir)/ganglia.inc
+
+AM_CFLAGS = -D_LARGEFILE64_SOURCE -I$(top_builddir)/include -I$(top_builddir)/lib
+
+if STATIC_BUILD
+noinst_LTLIBRARIES = libmodphp.la
+libmodphp_la_SOURCES = mod_php.c
+libmodphp_la_LDFLAGS = -export-all-symbols
+else
+pkglib_LTLIBRARIES = modphp.la
+modphp_la_SOURCES = mod_php.c
+modphp_la_LDFLAGS = -module -avoid-version -lphp@PHP_VERSION@
+
+EXTRA_DIST = README.in ../conf.d/modphp.conf.in
+endif
+
+README: README.in $(FIXCONFIG)
+ $(FIXCONFIG) README.in
+
+../conf.d/modphp.conf: ../conf.d/modphp.conf.in $(FIXCONFIG)
+ $(FIXCONFIG) ../conf.d/modphp.conf.in
+
+# Note that README is listed as a dependency to be generated, but it
+# is not currently installed anywhere
+install-exec-hook: ../conf.d/modphp.conf README
+ mkdir -p $(DESTDIR)$(sysconfdir)/conf.d && \
+ $(INSTALL_DATA) ../conf.d/modphp.conf $(DESTDIR)$(sysconfdir)/conf.d/modphp.conf
+
+INCLUDES = @APR_INCLUDES@ @PHP_INCLUDES@
View
161 gmond/modules/php/README.in
@@ -0,0 +1,161 @@
+How To Write And Use A PHP Metric
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The following sections will describe how to develop and deploy
+new metric modules using php. The PHP Embed is required, if you
+installed php from the sources make sure you compiled it with
+the configure option --enable-embed.
+
+Note : the current implementation of the PHP module for ganglia
+parse the php script each time a metric is gathered.
+
+Writing a php metric module
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are two functions that must be included in every php based
+metric module as well as at least one callback handler.
+These functions are:
+
+function metric_init($params) { ... }
+
+ This function will be called once at initialization time. It
+ can be used to do any kind of initialization that the module
+ requires in order to properly gather the intended metric. This
+ function also takes a single array type parameter which
+ contains configuration directives that were designated for
+ this module in the gmond.conf file. In addition to any other
+ initialization that is done, the function must also create,
+ populate and return the metric description array.
+ Each metric description array, must supply at least the
+ following elements:
+
+ $d = array(
+ 'name' => '<your_metric_name>',
+ 'call_back' => '<metric_handler_function>',
+ 'time_max' => int(<your_time_max>),
+ 'value_type' => '<string | uint | float | double>',
+ 'units' => '<your_units>',
+ 'slope' => '<zero | positive | negative | both>',
+ 'format' => '<your_format>',
+ 'description' => '<your_description>'
+ );
+
+ These elements are basically the same type of data that must be
+ supplied to the gmetric commandline utility with the exception
+ of the call_back function. See the gmetric help document for
+ more information. The call_back entry designates a function
+ that will be called whenever the data for this metric needs to
+ be gathered. The format of this function is very simple. It
+ is just a function name with a 'name' parameter
+
+ function My_Metric_Handler($name) { ... }
+
+ The value of the name parameter will be the name of the metric
+ that is being gathered. This allows a call_back function to
+ handle more than one metric and to be able to determine which
+ metric is being handled when called. This function implements the
+ code that handles the gathering of the metric data. The return
+ value from this function must match the data value type that was
+ specified in the corresponding metric description that was defined
+ during the metric_init() function.
+
+function metric_cleanup() { ... }
+
+ This function will be called once when gmond is shutting down.
+ Any module clean up code can be executed here and the function
+ must not return a value.
+
+Other than the mandatory functions and metric description array as
+specified above, the metric module is free to do whatever it needs
+in order to appropriately gather the intended metric data. Each
+metric description array can also contain additional user defined
+elements outside of the mandatory elements listed above. Any additional
+elements in the array will be ignored by mod_php but can to used by
+the php module itself to hold additional data that should correspond
+to the metric.
+
+Deploying a php metric module
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once a php metric module has been developed, deploying the module is as
+easy as copying a file to a specific directory. In the configuration of
+the mod_php module that is part of gmond.conf, a php metric module
+directory must have been specified (see next section). To deploy a
+php module, simply copy the .php file to the specified php module
+directory. Once the php module has been copied, start gmond using the
+-m parameter. This will show a list of all of the available metrics that
+gmond is capable of gathering. The list should include the name of the
+php module that was just deployed. Add this metric to a collection
+group, just like any other gmond metric.
+
+Configuring Gmond for php support
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Configuring Gmond to support php based modules is as easy as loading
+any other type of gmond dynamically loadable metric module. The name of
+the php support module is modphp.so. A base configuration file
+is included in the Ganglia source code and should have been installed
+automatically if done through an RPM. The basic configuration that
+should be added to or included by gmond.conf in your system is as
+follows:
+
+ modules {
+ module {
+ name = "php_module"
+ path = "modphp.so"
+ param php_modules_path {
+ value = "@moduledir@/php_modules"
+ }
+ /*
+ param php_ini_path {
+ value = "/etc/php.ini"
+ }
+ */
+ }
+ }
+
+ include ("@sysconfdir@/conf.d/*.phpconf")
+
+The php module use two config parameters. The most important one is the
+php_modules_path. The path that has been assigned to this directive will
+be passed down to the mod_php module and used to search for .php
+php modules. Any php module that is found in this location will be loaded
+and assumed to be a metric gathering module. If the collection groups that
+include metrics that are implemented in php, are put into separate
+configuration files and the file extension of the configuration files are
+.phpconf, they will automatically be loaded and read whenever the mod_php
+module is included.
+
+Configuring a Gmond php module
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The configuration of a Gmond php module is really no different
+than any other module. Most php modules will not require any
+specialized configuration at all. However, support has been
+provided to allow for parameters to be passed from the gmond.conf
+file to a php module. The syntax for passing parameters to
+a specific php module is at follows:
+
+ modules {
+ module {
+ name = "example"
+ language = "python"
+ param YouNameIt {
+ value = Whatever
+ }
+ param Another {
+ value = NewValue
+ }
+ }
+ }
+
+The "module" section must contain a "name" directive and a
+"language" directive. The value of the "name" directive must
+match the file name of the php module's .php file and the
+value of the "language" directive must be "php". The section
+can take multiple "param" sections. Each "param" section must
+include a name which will be the name of the value when it is
+passed to the php module, and a "value" directive. The
+"value" directive will always be passed as a string to the
+php module. It is up to the module itself to determine the
+actual value type.
View
725 gmond/modules/php/mod_php.c
@@ -0,0 +1,725 @@
+/*******************************************************************************
+*
+* This code is part of a php module for ganglia.
+*
+* Author : Nicolas Brousse (nicolas brousse.info)
+*
+* Portions Copyright (C) 2007 Novell, Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*
+* - Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+*
+* - Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+*
+* - Neither the name of Novell, Inc. nor the names of its
+* contributors may be used to endorse or promote products derived from this
+* software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+* ARE DISCLAIMED. IN NO EVENT SHALL Novell, Inc. OR THE CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+* POSSIBILITY OF SUCH DAMAGE.
+******************************************************************************/
+
+#include <gm_metric.h>
+#include <gm_msg.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "file.h"
+
+#include <apr_tables.h>
+#include <apr_strings.h>
+#include <apr_lib.h>
+
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <sapi/embed/php_embed.h>
+
+#ifdef ZTS
+ void ***tsrm_ls;
+#endif
+
+#define php_verbose_debug(debug_level, ...) { \
+ if (get_debug_msg_level() > debug_level) { \
+ debug_msg(__VA_ARGS__); \
+ } \
+}
+
+/*
+ * Declare ourselves so the configuration routines can find and know us.
+ * We'll fill it in at the end of the module.
+ */
+mmodule php_module;
+
+typedef struct
+{
+ zval *phpmod; /* The php metric module object */
+ char *callback; /* The metric call back function */
+ char *mod_name; /* The name */
+ char *script; /* PHP script filename to run */
+}
+mapped_info_t;
+
+typedef struct
+{
+ char mname[128];
+ int tmax;
+ char vtype[32];
+ char units[64];
+ char slope[32];
+ char format[64];
+ char desc[512];
+ char groups[512];
+ apr_table_t *extra_data;
+ char *callback;
+}
+php_metric_init_t;
+
+static apr_pool_t *pool;
+
+static apr_array_header_t *metric_info = NULL;
+static apr_array_header_t *metric_mapping_info = NULL;
+static apr_status_t php_metric_cleanup ( void *data);
+
+char modname_bfr[PATH_MAX];
+static char* is_php_module(const char* fname)
+{
+ php_verbose_debug(3, "is_php_module");
+ char* p = strrchr(fname, '.');
+ if (!p) {
+ return NULL;
+ }
+
+ if (strcmp(p, ".php")) {
+ return NULL;
+ }
+
+ strncpy(modname_bfr, fname, p-fname);
+ modname_bfr[p-fname] = 0;
+ return modname_bfr;
+}
+
+static void fill_metric_info(zval* dict, php_metric_init_t* minfo, char *modname, apr_pool_t *pool)
+{
+ char *metric_name = "";
+ char *key;
+ uint keylen;
+ ulong idx;
+ HashPosition pos;
+ zval **current;
+
+ php_verbose_debug(3, "fill_metric_info");
+
+ memset(minfo, 0, sizeof(*minfo));
+
+ /* create the apr table here */
+ minfo->extra_data = apr_table_make(pool, 2);
+
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(dict), &pos);
+ for(;; zend_hash_move_forward_ex(Z_ARRVAL_P(dict), &pos)) {
+
+ if (zend_hash_get_current_key_ex(Z_ARRVAL_P(dict), &key, &keylen, &idx, 0, &pos) == HASH_KEY_NON_EXISTANT)
+ break;
+
+ if (zend_hash_get_current_data_ex(Z_ARRVAL_P(dict), (void**) &current, &pos) == FAILURE) {
+ err_msg("[PHP] Can't get data for key [%s] in php module [%s].\n", key, modname);
+ continue;
+ }
+
+ if (!strcasecmp(key, "name")) {
+ if (!strncpy(minfo->mname, Z_STRVAL_PP(current), sizeof(minfo->mname))) {
+ err_msg("[PHP] No metric name given in php module [%s].\n", modname);
+ }
+ else
+ metric_name = minfo->mname;
+ continue;
+ }
+
+ if (!strcasecmp(key, "call_back")) {
+ if (!(minfo->callback = pestrndup(Z_STRVAL_PP(current), Z_STRLEN_PP(current), 1))) {
+ err_msg("[PHP] No php call back given for metric [%s] in module [%s]. Will not call\n",
+ metric_name, modname);
+ }
+ continue;
+ }
+
+ if (!strcasecmp(key, "time_max")) {
+ if (!(minfo->tmax = (int) Z_LVAL_PP(current))) {
+ minfo->tmax = 60;
+ err_msg("[PHP] No time max given for metric [%s] in module [%s]. Using %d.\n",
+ metric_name, modname, minfo->tmax);
+ }
+ continue;
+ }
+
+ if (!strcasecmp(key, "value_type")) {
+ if (!strncpy(minfo->vtype, Z_STRVAL_PP(current), sizeof(minfo->vtype))) {
+ strcpy (minfo->vtype, "uint");
+ err_msg("[PHP] No value type given for metric [%s] in module [%s]. Using %s.\n",
+ metric_name, modname, minfo->vtype);
+ }
+ continue;
+ }
+
+ if (!strcasecmp(key, "units")) {
+ if (!strncpy(minfo->units, Z_STRVAL_PP(current), sizeof(minfo->units))) {
+ strcpy (minfo->units, "unknown");
+ err_msg("[PHP] No metric units given for metric [%s] in module [%s]. Using %s.\n",
+ metric_name, modname, minfo->units);
+ }
+ continue;
+ }
+
+ if (!strcasecmp(key, "slope")) {
+ if (!strncpy(minfo->slope, Z_STRVAL_PP(current), sizeof(minfo->slope))) {
+ strcpy (minfo->slope, "both");
+ err_msg("[PHP] No slope given for metric [%s] in module [%s]. Using %s.\n",
+ metric_name, modname, minfo->slope);
+ }
+ continue;
+ }
+
+ if (!strcasecmp(key, "format")) {
+ if (!strncpy(minfo->format, Z_STRVAL_PP(current), sizeof(minfo->format))) {
+ strcpy (minfo->format, "%u");
+ err_msg("[PHP] No format given for metric [%s] in module [%s]. Using %s.\n",
+ metric_name, modname, minfo->format);
+ }
+ continue;
+ }
+
+ if (!strcasecmp(key, "description")) {
+ if (!strncpy(minfo->desc, Z_STRVAL_PP(current), sizeof(minfo->desc))) {
+ strcpy (minfo->desc, "unknown metric");
+ err_msg("[PHP] No description given for metric [%s] in module [%s]. Using %s.\n",
+ metric_name, modname, minfo->desc);
+ }
+ continue;
+ }
+
+ if (!strcasecmp(key, "groups")) {
+ if (!strncpy(minfo->groups, Z_STRVAL_PP(current), sizeof(minfo->groups))) {
+ strcpy (minfo->groups, "");
+ }
+ continue;
+ }
+
+ if (Z_TYPE_PP(current) == IS_LONG || Z_TYPE_PP(current) == IS_DOUBLE ||
+ Z_TYPE_PP(current) == IS_BOOL || Z_TYPE_PP(current) == IS_STRING) {
+ convert_to_string(*current);
+ apr_table_add(minfo->extra_data, key, Z_STRVAL_P(*current));
+ }
+ else {
+ err_msg("[PHP] Extra data key [%s] could not be processed.\n", key);
+ }
+
+ }
+
+ php_verbose_debug(3, "name: %s", minfo->mname);
+ php_verbose_debug(3, "callback: %s", minfo->callback);
+ php_verbose_debug(3, "time_max: %d", minfo->tmax);
+ php_verbose_debug(3, "value_type: %s", minfo->vtype);
+ php_verbose_debug(3, "units: %s", minfo->units);
+ php_verbose_debug(3, "slope: %s", minfo->slope);
+ php_verbose_debug(3, "format: %s", minfo->format);
+ php_verbose_debug(3, "description: %s", minfo->desc);
+ php_verbose_debug(3, "groups: %s", minfo->groups);
+
+}
+
+static void fill_gmi(Ganglia_25metric* gmi, php_metric_init_t* minfo)
+{
+ char *s, *lasts;
+ int i;
+ const apr_array_header_t *arr = apr_table_elts(minfo->extra_data);
+ const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
+
+ php_verbose_debug(3, "fill_gmi");
+
+ /* gmi->key will be automatically assigned by gmond */
+ gmi->name = apr_pstrdup (pool, minfo->mname);
+ gmi->tmax = minfo->tmax;
+ if (!strcasecmp(minfo->vtype, "string")) {
+ gmi->type = GANGLIA_VALUE_STRING;
+ gmi->msg_size = UDP_HEADER_SIZE+MAX_G_STRING_SIZE;
+ }
+ else if (!strcasecmp(minfo->vtype, "uint")) {
+ gmi->type = GANGLIA_VALUE_UNSIGNED_INT;
+ gmi->msg_size = UDP_HEADER_SIZE+8;
+ }
+ else if (!strcasecmp(minfo->vtype, "int")) {
+ gmi->type = GANGLIA_VALUE_INT;
+ gmi->msg_size = UDP_HEADER_SIZE+8;
+ }
+ else if (!strcasecmp(minfo->vtype, "float")) {
+ gmi->type = GANGLIA_VALUE_FLOAT;
+ gmi->msg_size = UDP_HEADER_SIZE+8;
+ }
+ else if (!strcasecmp(minfo->vtype, "double")) {
+ gmi->type = GANGLIA_VALUE_DOUBLE;
+ gmi->msg_size = UDP_HEADER_SIZE+16;
+ }
+ else {
+ gmi->type = GANGLIA_VALUE_UNKNOWN;
+ gmi->msg_size = UDP_HEADER_SIZE+8;
+ }
+
+ gmi->units = apr_pstrdup(pool, minfo->units);
+ gmi->slope = apr_pstrdup(pool, minfo->slope);
+ gmi->fmt = apr_pstrdup(pool, minfo->format);
+ gmi->desc = apr_pstrdup(pool, minfo->desc);
+
+ MMETRIC_INIT_METADATA(gmi, pool);
+ for (s=(char *)apr_strtok(minfo->groups, ",", &lasts);
+ s!=NULL; s=(char *)apr_strtok(NULL, ",", &lasts)) {
+ char *d = s;
+ /* Strip the leading white space */
+ while (d && *d && apr_isspace(*d)) {
+ d++;
+ }
+ MMETRIC_ADD_METADATA(gmi,MGROUP,d);
+ }
+
+ /* transfer any extra data as metric metadata */
+ for (i = 0; i < arr->nelts; ++i) {
+ if (elts[i].key == NULL)
+ continue;
+ MMETRIC_ADD_METADATA(gmi, elts[i].key, elts[i].val);
+ }
+}
+
+static cfg_t* find_module_config(char *modname)
+{
+ cfg_t *modules_cfg;
+ int j;
+
+ php_verbose_debug(3, "find_module_config");
+
+ modules_cfg = cfg_getsec(php_module.config_file, "modules");
+ for (j = 0; j < cfg_size(modules_cfg, "module"); j++) {
+ char *modName, *modLanguage;
+ int modEnabled;
+
+ cfg_t *phpmodule = cfg_getnsec(modules_cfg, "module", j);
+
+ /* Check the module language to make sure that
+ the language designation is PHP.
+ */
+ modLanguage = cfg_getstr(phpmodule, "language");
+ if (!modLanguage || strcasecmp(modLanguage, "php"))
+ continue;
+
+ modName = cfg_getstr(phpmodule, "name");
+ if (strcasecmp(modname, modName)) {
+ continue;
+ }
+
+ /* Check to make sure that the module is enabled.
+ */
+ modEnabled = cfg_getbool(phpmodule, "enabled");
+ if (!modEnabled)
+ continue;
+
+ return phpmodule;
+ }
+ return NULL;
+}
+
+static void build_params_dict(zval *params_dict, cfg_t *phpmodule)
+{
+ int k;
+
+ /* init params_dict as a ZVAL ARRAY */
+ array_init(params_dict);
+
+ if (phpmodule) {
+ for (k = 0; k < cfg_size(phpmodule, "param"); k++) {
+ cfg_t *param;
+ char *name, *value;
+
+ param = cfg_getnsec(phpmodule, "param", k);
+ name = apr_pstrdup(pool, param->title);
+ value = apr_pstrdup(pool, cfg_getstr(param, "value"));
+ if (name && value) {
+ add_assoc_string(params_dict, name, value, 1);
+ }
+ }
+ }
+}
+
+static int php_metric_init (apr_pool_t *p)
+{
+ DIR *dp;
+ struct dirent *entry;
+ char* modname;
+ php_metric_init_t minfo;
+ Ganglia_25metric *gmi;
+ mapped_info_t *mi;
+ const apr_array_header_t *php_module_params = php_module.module_params_list;
+ char* path = NULL;
+ char* php_ini_path = NULL;
+ cfg_t *module_cfg;
+ char *key;
+ uint keylen;
+ ulong idx;
+ HashPosition pos;
+ zval retval, funcname, *params, **current, **zval_vector[1];
+ zend_uint params_length;
+ zend_file_handle script;
+ char file[256];
+ int i, php_initialized = 0;
+
+ php_verbose_debug(3, "php_metric_init");
+ php_verbose_debug(3, "php_module_params size : %d", php_module_params->nelts);
+
+ for (i=0; i < php_module_params->nelts; ++i) {
+
+ mmparam node = ((mmparam *)php_module_params->elts)[i];
+
+ php_verbose_debug(3, "php_module_params: Key=%s, Value=%s", node.name, node.value);
+
+ if (!strcasecmp(node.name, "php_modules_path")) {
+ path = node.value;
+ php_verbose_debug(2, "php_modules path: %s", path);
+ } else if (!strcasecmp(node.name, "php_ini_path")) {
+ php_ini_path = node.value;
+ } else {
+ php_verbose_debug(1, "Unknown PHP module param : %s", node.name);
+ }
+
+ }
+
+ /* Allocate a pool that will be used by this module */
+ apr_pool_create(&pool, p);
+
+ metric_info = apr_array_make(pool, 10, sizeof(Ganglia_25metric));
+ metric_mapping_info = apr_array_make(pool, 10, sizeof(mapped_info_t));
+
+ /* Verify path exists and can be read */
+
+ if (!path) {
+ err_msg("[PHP] Missing php modules path.\n");
+ return -1;
+ }
+
+ if (access(path, F_OK)) {
+ /* 'path' does not exist */
+ err_msg("[PHP] Can't open the PHP module path %s.\n", path);
+ return -1;
+ }
+
+ if (access(path, R_OK)) {
+ /* Don't have read access to 'path' */
+ err_msg("[PHP] Can't read from the PHP module path %s.\n", path);
+ return -1;
+ }
+
+ /* Initialize each perl module */
+ if ((dp = opendir(path)) == NULL) {
+ /* Error: Cannot open the directory - Shouldn't happen */
+ /* Log? */
+ err_msg("[PHP] Can't open the PHP module path %s.\n", path);
+ return -1;
+ }
+
+ php_verbose_debug(3, "php_embed_init");
+ if (php_ini_path) {
+ if (access(php_ini_path, R_OK)) {
+ err_msg("[PHP] Can't read the php.ini : %s.\n", php_ini_path);
+ return -1;
+ } else {
+ php_verbose_debug(2, "Using php.ini : %s", php_ini_path);
+ php_embed_module.php_ini_path_override = php_ini_path;
+ }
+ }
+ if (php_embed_init(0, NULL PTSRMLS_CC) != SUCCESS) {
+ err_msg("[PHP] Can't start the PHP engine.");
+ return -1;
+ }
+ php_verbose_debug(2, ">>> started php sapi %s", sapi_module.name);
+
+ while ((entry = readdir(dp)) != NULL) {
+
+ modname = is_php_module(entry->d_name);
+
+ if (modname == NULL)
+ continue;
+
+ /* Find the specified module configuration in gmond.conf
+ If this return NULL then either the module config
+ doesn't exist or the module is disabled. */
+ module_cfg = find_module_config(modname);
+ if (!module_cfg)
+ continue;
+
+ /* start php engine if not started yet */
+ zend_try {
+ if (!php_initialized) {
+ php_initialized = 1;
+ } else {
+ php_request_startup(TSRMLS_C);
+ }
+ } zend_end_try();
+
+ strcpy(file, path);
+ strcat(file, "/");
+ strcat(file, modname);
+ strcat(file, ".php");
+
+ script.type = ZEND_HANDLE_FP;
+ script.filename = pestrndup((char *)&file, sizeof(file), 1);
+ script.opened_path = NULL;
+ script.free_filename = 0;
+ if (!(script.handle.fp = fopen(script.filename, "rb"))) {
+ err_msg("Unable to open %s\n", script.filename);
+ continue;
+ }
+
+ php_verbose_debug(2, ">>> execute php script %s", script.filename);
+ php_execute_script(&script TSRMLS_CC);
+
+ /* Build a parameter dictionary to pass to the module */
+ MAKE_STD_ZVAL(params);
+ build_params_dict(params, module_cfg);
+ if (!params || Z_TYPE_P(params) != IS_ARRAY) {
+ /* No metric_init function. */
+ err_msg("[PHP] Can't build the parameters array for [%s].\n", modname);
+ continue;
+ }
+ php_verbose_debug(3, "built the parameters dictionnary for the php module [%s]", modname);
+
+ ZVAL_STRING(&funcname, "metric_init", 0);
+ params_length = zend_hash_num_elements(Z_ARRVAL_P(params));
+ php_verbose_debug(2, "found %d params for the php module [%s]", params_length, modname);
+
+ /* Convert params to zval vector */
+ zval_vector[0] = &params;
+
+ /* Now call the metric_init method of the python module */
+ if (call_user_function(EG(function_table), NULL, &funcname, &retval,
+ 1, *zval_vector TSRMLS_CC) == FAILURE) {
+ /* failed calling metric_init */
+ err_msg("[PHP] Can't call the metric_init function in the php module [%s]\n", modname);
+ continue;
+ }
+
+ php_verbose_debug(3, "called the metric_init function for the php module [%s]\n", modname);
+
+ if (Z_TYPE_P(&retval) == IS_ARRAY) {
+ php_verbose_debug(2, "get %d descriptors for the php module [%s]",
+ zend_hash_num_elements(Z_ARRVAL(retval)), modname);
+
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL(retval), &pos);
+ while (zend_hash_get_current_data_ex(Z_ARRVAL(retval), (void**)&current, &pos) == SUCCESS) {
+
+ if (zend_hash_get_current_key_ex(Z_ARRVAL(retval), &key, &keylen, &idx, 0, &pos) == HASH_KEY_NON_EXISTANT)
+ break;
+
+ if (Z_TYPE_PP(current) == IS_ARRAY) {
+ fill_metric_info(*current, &minfo, modname, pool);
+ php_verbose_debug(3, "metric info [%s] (callback : %s)", modname, minfo.callback);
+ gmi = (Ganglia_25metric*)apr_array_push(metric_info);
+ fill_gmi(gmi, &minfo);
+ mi = (mapped_info_t*)apr_array_push(metric_mapping_info);
+ mi->phpmod = *current;
+ mi->script = script.filename;
+ mi->mod_name = apr_pstrdup(pool, modname);
+ mi->callback = minfo.callback;
+ }
+
+ zend_hash_move_forward_ex(Z_ARRVAL(retval), &pos);
+ }
+ }
+
+ zval_dtor(&retval);
+ zval_ptr_dtor(&params);
+
+ zend_try {
+ php_request_shutdown(NULL);
+ } zend_end_try();
+
+ }
+ closedir(dp);
+
+ apr_pool_cleanup_register(pool, NULL,
+ php_metric_cleanup,
+ apr_pool_cleanup_null);
+
+ /* Replace the empty static metric definition array with the
+ dynamic array that we just created
+ */
+ gmi = apr_array_push(metric_info);
+ memset (gmi, 0, sizeof(*gmi));
+ mi = apr_array_push(metric_mapping_info);
+ memset (mi, 0, sizeof(*mi));
+
+ php_module.metrics_info = (Ganglia_25metric *)metric_info->elts;
+
+ return 0;
+}
+
+static apr_status_t php_metric_cleanup ( void *data)
+{
+ mapped_info_t *mi, *smi;
+ int i, j;
+
+ php_verbose_debug(3, "php_metric_cleanup");
+
+ mi = (mapped_info_t*) metric_mapping_info->elts;
+ for (i = 0; i < metric_mapping_info->nelts; i++) {
+ if (mi[i].phpmod) {
+ //efree(mi[i].callback);
+ //pefree(mi[i].script, 1);
+ zval_ptr_dtor(&mi[i].phpmod);
+
+ /* Set all modules that fall after this once with the same
+ * module pointer to NULL so metric_cleanup only gets called
+ * once on the module.
+ */
+ smi = (mapped_info_t*) metric_mapping_info->elts;
+ for (j = i+1; j < metric_mapping_info->nelts; j++) {
+ if (smi[j].phpmod == mi[i].phpmod) {
+ smi[j].phpmod = NULL;
+ }
+ }
+ }
+ }
+
+ php_module_shutdown(TSRMLS_C);
+ sapi_shutdown();
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ if (php_embed_module.ini_entries) {
+ free(php_embed_module.ini_entries);
+ php_embed_module.ini_entries = NULL;
+ }
+
+ return APR_SUCCESS;
+}
+
+static g_val_t php_metric_handler( int metric_index )
+{
+ zval retval, funcname, *tmp, **zval_vector[1];
+ zend_file_handle script;
+ g_val_t val;
+ Ganglia_25metric *gmi = (Ganglia_25metric *) metric_info->elts;
+ mapped_info_t *mi = (mapped_info_t*) metric_mapping_info->elts;
+
+ php_request_startup(TSRMLS_C);
+
+ php_verbose_debug(3, "php_metric_handler");
+
+ memset(&val, 0, sizeof(val));
+ if (!mi[metric_index].callback) {
+ /* No call back provided for this metric */
+ return val;
+ }
+
+ php_verbose_debug(3, ">>> metric index : %d, callback : %s", metric_index, (char *) mi[metric_index].callback);
+
+ script.type = ZEND_HANDLE_FP;
+ script.filename = mi[metric_index].script;
+ script.opened_path = NULL;
+ script.free_filename = 0;
+ if (!(script.handle.fp = fopen(script.filename, "rb"))) {
+ err_msg("Unable to open %s\n", script.filename);
+ return val;
+ }
+
+ php_verbose_debug(2, ">>> execute php script %s", script.filename);
+
+ php_execute_script(&script TSRMLS_CC);
+
+ ZVAL_STRING(&funcname, mi[metric_index].callback, 0);
+ MAKE_STD_ZVAL(tmp);
+ ZVAL_STRING(tmp, gmi[metric_index].name, 1);
+ zval_vector[0] = &tmp;
+
+ /* Call the metric handler call back for this metric */
+ if (call_user_function(EG(function_table), NULL, &funcname, &retval,
+ 1, *zval_vector TSRMLS_CC) == FAILURE) {
+ /* failed calling metric_init */
+ err_msg("[PHP] Can't call the metric handler function [%s] for [%s] in the php module [%s].\n",
+ &funcname, gmi[metric_index].name, mi[metric_index].mod_name);
+ return val;
+ }
+ php_verbose_debug(3, "Called the metric handler function [%s] for [%s] in the php module [%s].\n",
+ mi[metric_index].callback, gmi[metric_index].name, mi[metric_index].mod_name);
+
+ switch (gmi[metric_index].type) {
+ case GANGLIA_VALUE_STRING:
+ {
+ convert_to_string(&retval);
+ strcpy(val.str, Z_STRVAL_P(&retval));
+ php_verbose_debug(3, "string: %s", val.str);
+ break;
+ }
+ case GANGLIA_VALUE_UNSIGNED_INT:
+ {
+ convert_to_long(&retval);
+ val.uint32 = (unsigned int) Z_LVAL_P(&retval);
+ php_verbose_debug(3, "uint: %i", val.uint32);
+ break;
+ }
+ case GANGLIA_VALUE_INT:
+ {
+ convert_to_long(&retval);
+ val.int32 = (int) Z_LVAL_P(&retval);
+ php_verbose_debug(3, "int: %i", val.int32);
+ break;
+ }
+ case GANGLIA_VALUE_FLOAT:
+ {
+ convert_to_double(&retval);
+ val.f = Z_DVAL_P(&retval);
+ php_verbose_debug(3, "float: %d", val.f);
+ break;
+ }
+ case GANGLIA_VALUE_DOUBLE:
+ {
+ convert_to_double(&retval);
+ val.d = Z_DVAL_P(&retval);
+ php_verbose_debug(3, "double: %d", val.d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ zval_ptr_dtor(&tmp);
+
+ php_request_shutdown(NULL);
+
+ return val;
+}
+
+mmodule php_module =
+{
+ STD_MMODULE_STUFF,
+ php_metric_init,
+ NULL,
+ NULL, /* defined dynamically */
+ php_metric_handler,
+};
View
2 gmond/php_modules/Makefile.am
@@ -0,0 +1,2 @@
+DIST_SUBDIRS = example
+EXTRA_DIST = ./conf.d/*.phpconf
View
32 gmond/php_modules/conf.d/example.phpconf
@@ -0,0 +1,32 @@
+modules {
+ module {
+ name = "example"
+ language = "php"
+ param RandomMax {
+ value = 600
+ }
+ param ConstantValue {
+ value = 112
+ }
+ }
+}
+
+#/* Collection groups for the
+# example php module */
+collection_group {
+ collect_every = 10
+ time_threshold = 50
+ metric {
+ name = "PHP_Random_Numbers"
+ value_threshold = 1.0
+ }
+}
+
+collection_group {
+ collect_once = yes
+ time_threshold = 20
+ metric {
+ name = "PHP_Constant_Number"
+ }
+}
+
View
2 gmond/php_modules/example/Makefile.am
@@ -0,0 +1,2 @@
+phpscripts = example.php
+EXTRA_DIST = $(phpscripts)
View
121 gmond/php_modules/example/example.php
@@ -0,0 +1,121 @@
+<?php
+/*******************************************************************************
+*
+* This code is part of a php module for ganglia.
+*
+* Author : Nicolas Brousse (nicolas brousse.info)
+*
+* Portions Copyright (C) 2007 Novell, Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*
+* - Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+*
+* - Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+*
+* - Neither the name of Novell, Inc. nor the names of its
+* contributors may be used to endorse or promote products derived from this
+* software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+* ARE DISCLAIMED. IN NO EVENT SHALL Novell, Inc. OR THE CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+* POSSIBILITY OF SUCH DAMAGE.
+******************************************************************************/
+
+$descriptors = array();
+$Random_Max = 50;
+$Constant_Value = 50;
+
+function Random_Numbers($name) {
+ // Return a random number.
+ global $Random_Max;
+ echo "[php example] Random_Numbers\n";
+ return rand(0, $Random_Max);
+}
+
+function Constant_Number($name) {
+ // Return a constant number.
+ global $Constant_Value;
+ echo "[php example] Constant_Number\n";
+ return (int) $Constant_Value;
+}
+
+function metric_init($params) {
+ /* Initialize the random number generator and create the
+ metric definition dictionary object for each metric. */
+ global $descriptors;
+ global $Random_Max;
+ global $Constant_Value;
+
+ echo "[php example] Received the following parameters:\n";
+ var_dump($params);
+
+ if (in_array('RandomMax', $params)) {
+ $Random_Max = (int) $params['RandomMax'];
+ }
+
+ if (in_array('ConstantValue', $params)) {
+ $Constant_Value = (int) $params['ConstantValue'];
+ }
+
+ $d1 = array(
+ 'name' => 'PHP_Random_Numbers',
+ 'call_back' => "Random_Numbers",
+ 'time_max' => 90,
+ 'value_type' => 'uint',
+ 'units' => 'N',
+ 'slope' => 'both',
+ 'format' => '%u',
+ 'description' => 'Example module metric (random numbers)',
+ 'groups' => 'example,random'
+ );
+
+ $d2 = array(
+ 'name' => 'PHP_Constant_Number',
+ 'call_back' => "Constant_Number",
+ 'time_max' => 90,
+ 'value_type' => 'uint',
+ 'units' => 'N',
+ 'slope' => 'zero',
+ 'format' => '%hu',
+ 'description' => 'Example module metric (constant number)'
+ );
+
+ $descriptors = array($d1, $d2);
+
+ echo "[php example] Returning descriptors :\n";
+ var_dump($descriptors);
+
+ return $descriptors;
+}
+
+function metric_cleanup() {
+ // Clean up the metric module.
+ echo "[php example] metric cleanup\n";
+}
+
+#This code is for debugging and unit testing
+if (php_sapi_name() != 'embed') {
+ print "Non-embed mode\n";
+ $params = array(
+ 'RandomMax' => '500',
+ 'ConstantValue' => '322'
+ );
+ metric_init($params);
+ foreach ($descriptors as $d) {
+ $v = $d['call_back']($d['name']);
+ printf("value for %s is %u\n", $d['name'], $v);
+ }
+}

0 comments on commit 4aa9dc4

Please sign in to comment.