summaryrefslogtreecommitdiffstats
path: root/lib/plugins/lrm
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:40:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:40:13 +0000
commite9be59e1502a41bab9891d96d753102a7dafef0b (patch)
treec3b2da87c414881f4b53d0964f407c83492d813e /lib/plugins/lrm
parentInitial commit. (diff)
downloadcluster-glue-e9be59e1502a41bab9891d96d753102a7dafef0b.tar.xz
cluster-glue-e9be59e1502a41bab9891d96d753102a7dafef0b.zip
Adding upstream version 1.0.12.upstream/1.0.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/plugins/lrm')
-rw-r--r--lib/plugins/lrm/Makefile.am58
-rw-r--r--lib/plugins/lrm/dbus/Makefile.am16
-rw-r--r--lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml45
-rw-r--r--lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml71
-rw-r--r--lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml57
-rw-r--r--lib/plugins/lrm/raexechb.c416
-rw-r--r--lib/plugins/lrm/raexeclsb.c609
-rw-r--r--lib/plugins/lrm/raexecocf.c496
-rw-r--r--lib/plugins/lrm/raexecupstart.c222
-rw-r--r--lib/plugins/lrm/upstart-dbus.c406
-rw-r--r--lib/plugins/lrm/upstart-dbus.h36
11 files changed, 2432 insertions, 0 deletions
diff --git a/lib/plugins/lrm/Makefile.am b/lib/plugins/lrm/Makefile.am
new file mode 100644
index 0000000..fd24579
--- /dev/null
+++ b/lib/plugins/lrm/Makefile.am
@@ -0,0 +1,58 @@
+#
+# Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+# Copyright (c) 2004 International Business Machines
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+if UPSTART
+SUBDIRS = dbus
+endif
+
+LRM_DIR = lrm
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+if UPSTART
+INCLUDES += $(DBUS_CFLAGS)
+endif
+
+halibdir = $(libdir)/@HB_PKG@
+havarlibdir = $(localstatedir)/lib/@HB_PKG@
+COMMONLIBS = $(top_builddir)/lib/clplumbing/libplumb.la \
+ $(top_builddir)/lib/lrm/liblrm.la \
+ $(GLIBLIB)
+
+plugindir = $(halibdir)/plugins/RAExec
+
+plugin_LTLIBRARIES = lsb.la ocf.la heartbeat.la
+if UPSTART
+plugin_LTLIBRARIES += upstart.la
+endif
+
+lsb_la_SOURCES = raexeclsb.c
+lsb_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version
+
+ocf_la_SOURCES = raexecocf.c
+ocf_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version
+
+heartbeat_la_SOURCES = raexechb.c
+heartbeat_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version
+
+if UPSTART
+upstart_la_SOURCES = raexecupstart.c upstart-dbus.c
+upstart_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version \
+ $(DBUS_LIBS)
+endif
diff --git a/lib/plugins/lrm/dbus/Makefile.am b/lib/plugins/lrm/dbus/Makefile.am
new file mode 100644
index 0000000..ec93436
--- /dev/null
+++ b/lib/plugins/lrm/dbus/Makefile.am
@@ -0,0 +1,16 @@
+if UPSTART
+BINDINGS=Upstart_Instance.h \
+ Upstart_Job.h \
+ Upstart.h
+
+all-local:
+ for header in $(BINDINGS); do \
+ input=com.ubuntu.`echo $$header | sed 's/\.h//' | tr _ .`.xml; \
+ $(DBUS_BINDING_TOOL) --mode=glib-client $$input > $$header; \
+ done
+
+clean-local:
+ rm -f $(BINDINGS)
+
+EXTRA_DIST = *.xml
+endif
diff --git a/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml
new file mode 100644
index 0000000..d4f7ab2
--- /dev/null
+++ b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- upstart
+
+ com.ubuntu.Upstart.Instance.xml - interface definition for interface
+ objects
+
+ Copyright © 2009 Canonical Ltd.
+ Author: Scott James Remnant <scott@netsplit.com>.
+
+ This file is free software; Canonical Ltd gives unlimited permission
+ to copy and/or distribute it, with or without modifications, as long
+ as this notice is preserved.
+
+ Communication and interaction with Upstart through this interface is
+ permitted without restriction.
+ -->
+
+<!DOCTYPE node PUBLIC
+ "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<node>
+ <interface name="com.ubuntu.Upstart0_6.Instance">
+ <!-- Methods to directly control instances. Unlike the equivalent methods
+ for a Job, these are not permitted to pass or set environment. -->
+ <method name="Start">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+ <method name="Stop">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+ <method name="Restart">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+
+ <!-- Basic information about an Instance -->
+ <property name="name" type="s" access="read" />
+ <property name="goal" type="s" access="read" />
+ <property name="state" type="s" access="read" />
+ <property name="processes" type="a(si)" access="read" />
+ </interface>
+</node>
diff --git a/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml
new file mode 100644
index 0000000..27f47a1
--- /dev/null
+++ b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- upstart
+
+ com.ubuntu.Upstart.Job.xml - interface definition for job objects
+
+ Copyright © 2009 Canonical Ltd.
+ Author: Scott James Remnant <scott@netsplit.com>.
+
+ This file is free software; Canonical Ltd gives unlimited permission
+ to copy and/or distribute it, with or without modifications, as long
+ as this notice is preserved.
+
+ Communication and interaction with Upstart through this interface is
+ permitted without restriction.
+ -->
+
+<!DOCTYPE node PUBLIC
+ "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<node>
+ <interface name="com.ubuntu.Upstart0_6.Job">
+ <!-- Get object paths for instances, while you can figure these out too,
+ it's still better form to use these -->
+ <method name="GetInstance">
+ <arg name="env" type="as" direction="in" />
+ <arg name="instance" type="o" direction="out" />
+ </method>
+ <method name="GetInstanceByName">
+ <arg name="name" type="s" direction="in" />
+ <arg name="instance" type="o" direction="out" />
+ </method>
+ <method name="GetAllInstances">
+ <arg name="instances" type="ao" direction="out" />
+ </method>
+
+ <!-- Signals for changes to the instance list for a job -->
+ <signal name="InstanceAdded">
+ <arg name="instance" type="o" />
+ </signal>
+ <signal name="InstanceRemoved">
+ <arg name="instance" type="o" />
+ </signal>
+
+ <!-- Job control; the environment arguments are used for both instance
+ selection and for passing environment to the processes of the job. -->
+ <method name="Start">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="env" type="as" direction="in" />
+ <arg name="wait" type="b" direction="in" />
+ <arg name="instance" type="o" direction="out" />
+ </method>
+ <method name="Stop">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="env" type="as" direction="in" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+ <method name="Restart">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="env" type="as" direction="in" />
+ <arg name="wait" type="b" direction="in" />
+ <arg name="instance" type="o" direction="out" />
+ </method>
+
+ <!-- Basic information about a Job -->
+ <property name="name" type="s" access="read" />
+ <property name="description" type="s" access="read" />
+ <property name="author" type="s" access="read" />
+ <property name="version" type="s" access="read" />
+ </interface>
+</node>
diff --git a/lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml
new file mode 100644
index 0000000..a4331cd
--- /dev/null
+++ b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- upstart
+
+ com.ubuntu.Upstart.xml - interface definition for manager object
+
+ Copyright © 2009 Canonical Ltd.
+ Author: Scott James Remnant <scott@netsplit.com>.
+
+ This file is free software; Canonical Ltd gives unlimited permission
+ to copy and/or distribute it, with or without modifications, as long
+ as this notice is preserved.
+
+ Communication and interaction with Upstart through this interface is
+ permitted without restriction.
+ -->
+
+<!DOCTYPE node PUBLIC
+ "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<node name="/com/ubuntu/Upstart">
+ <interface name="com.ubuntu.Upstart0_6">
+ <!-- Reload all configuration sources -->
+ <method name="ReloadConfiguration">
+ </method>
+
+ <!-- Get object paths for jobs, while you can figure them out, it's
+ better form to use these -->
+ <method name="GetJobByName">
+ <arg name="name" type="s" direction="in" />
+ <arg name="job" type="o" direction="out" />
+ </method>
+ <method name="GetAllJobs">
+ <arg name="jobs" type="ao" direction="out" />
+ </method>
+
+ <!-- Signals for changes to the job list -->
+ <signal name="JobAdded">
+ <arg name="job" type="o" />
+ </signal>
+ <signal name="JobRemoved">
+ <arg name="job" type="o" />
+ </signal>
+
+ <!-- Event emission -->
+ <method name="EmitEvent">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="name" type="s" direction="in" />
+ <arg name="env" type="as" direction="in" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+
+ <!-- Basic information about Upstart -->
+ <property name="version" type="s" access="read" />
+ <property name="log_priority" type="s" access="readwrite" />
+ </interface>
+</node>
diff --git a/lib/plugins/lrm/raexechb.c b/lib/plugins/lrm/raexechb.c
new file mode 100644
index 0000000..f9f1eb9
--- /dev/null
+++ b/lib/plugins/lrm/raexechb.c
@@ -0,0 +1,416 @@
+/*
+ * 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.1 of the License, or (at your option) any later version.
+ *
+ * This software 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * File: raexechb.c
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This code implements the Resource Agent Plugin Module for LSB style.
+ * It's a part of Local Resource Manager. Currently it's used by lrmd only.
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <pils/plugin.h>
+#include <lrm/raexec.h>
+
+#define PIL_PLUGINTYPE RA_EXEC_TYPE
+#define PIL_PLUGIN heartbeat
+#define PIL_PLUGINTYPE_S "RAExec"
+#define PIL_PLUGIN_S "heartbeat"
+#define PIL_PLUGINLICENSE LICENSE_PUBDOM
+#define PIL_PLUGINLICENSEURL URL_PUBDOM
+
+static const char * RA_PATH = HB_RA_DIR;
+
+static const char meta_data_template[] =
+"<?xml version=\"1.0\"?>\n"
+"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
+"<resource-agent name=\"%s\">\n"
+"<version>1.0</version>\n"
+"<longdesc lang=\"en\">\n"
+"%s"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">%s</shortdesc>\n"
+"<parameters>\n"
+"<parameter name=\"1\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the first argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[1]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"<parameter name=\"2\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the second argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[2]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"<parameter name=\"3\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the third argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[3]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"<parameter name=\"4\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the fourth argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[4]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"<parameter name=\"5\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the fifth argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[5]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"</parameters>\n"
+"<actions>\n"
+"<action name=\"start\" timeout=\"15\" />\n"
+"<action name=\"stop\" timeout=\"15\" />\n"
+"<action name=\"status\" timeout=\"15\" />\n"
+"<action name=\"monitor\" timeout=\"15\" interval=\"15\" start-delay=\"15\" />\n"
+"<action name=\"meta-data\" timeout=\"5\" />\n"
+"</actions>\n"
+"<special tag=\"heartbeart\">\n"
+"</special>\n"
+"</resource-agent>\n";
+
+/* The begin of exported function list */
+static int execra(const char * rsc_id,
+ const char * rsc_type,
+ const char * provider,
+ const char * op_type,
+ const int timeout,
+ GHashTable * params);
+
+static uniform_ret_execra_t map_ra_retvalue(int ret_execra
+ , const char * op_type, const char * std_output);
+static int get_resource_list(GList ** rsc_info);
+static char* get_resource_meta(const char* rsc_type, const char* provider);
+static int get_provider_list(const char* ra_type, GList ** providers);
+
+/* The end of exported function list */
+
+/* The begin of internal used function & data list */
+#define MAX_PARAMETER_NUM 40
+typedef char * RA_ARGV[MAX_PARAMETER_NUM];
+
+static const int MAX_LENGTH_OF_RSCNAME = 40,
+ MAX_LENGTH_OF_OPNAME = 40;
+
+static int prepare_cmd_parameters(const char * rsc_type, const char * op_type,
+ GHashTable * params, RA_ARGV params_argv);
+/* The end of internal function & data list */
+
+/* Rource agent execution plugin operations */
+static struct RAExecOps raops =
+{ execra,
+ map_ra_retvalue,
+ get_resource_list,
+ get_provider_list,
+ get_resource_meta
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static void* OurImports;
+static void* interfprivate;
+static int idebuglevel = 0;
+
+/*
+ * Our plugin initialization and registration function
+ * It gets called when the plugin gets loaded.
+ */
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ if (getenv(HADEBUGVAL) != NULL && atoi(getenv(HADEBUGVAL)) > 0 ) {
+ idebuglevel = atoi(getenv(HADEBUGVAL));
+ cl_log(LOG_DEBUG, "LRM debug level set to %d", idebuglevel);
+ }
+
+ /* Register our interfaces */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S,
+ &raops, NULL, &OurInterface, &OurImports,
+ interfprivate);
+}
+
+/*
+ * Real work starts here ;-)
+ */
+
+static int
+execra( const char * rsc_id, const char * rsc_type, const char * provider,
+ const char * op_type, const int timeout, GHashTable * params)
+{
+ RA_ARGV params_argv;
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ uniform_ret_execra_t exit_value;
+ GString * debug_info;
+ char * optype_tmp = NULL;
+ int index_tmp = 0;
+
+ /* How to generate the meta-data? There is nearly no value
+ * information in meta-data build up in current way.
+ * Should directly add meta-data to the script itself?
+ */
+ if ( 0 == STRNCMP_CONST(op_type, "meta-data") ) {
+ printf("%s", get_resource_meta(rsc_type, provider));
+ exit(0);
+ }
+
+ /* To simulate the 'monitor' operation with 'status'.
+ * Now suppose there is no 'monitor' operation for heartbeat scripts.
+ */
+ if ( 0 == STRNCMP_CONST(op_type, "monitor") ) {
+ optype_tmp = g_strdup("status");
+ } else {
+ optype_tmp = g_strdup(op_type);
+ }
+
+ /* Prepare the call parameter */
+ if (0 > prepare_cmd_parameters(rsc_type, optype_tmp, params, params_argv)) {
+ cl_log(LOG_ERR, "HB RA: Error of preparing parameters");
+ g_free(optype_tmp);
+ return -1;
+ }
+ g_free(optype_tmp);
+
+ get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname);
+
+ /* let this log show only high loglevel. */
+ if (idebuglevel > 1) {
+ debug_info = g_string_new("");
+ do {
+ g_string_append(debug_info, params_argv[index_tmp]);
+ g_string_append(debug_info, " ");
+ } while (params_argv[++index_tmp] != NULL);
+ debug_info->str[debug_info->len-1] = '\0';
+
+ cl_log(LOG_DEBUG, "RA instance %s executing: heartbeat::%s"
+ , rsc_id, debug_info->str);
+
+ g_string_free(debug_info, TRUE);
+ }
+
+ closefiles(); /* don't leak open files */
+ execv(ra_pathname, params_argv);
+ cl_perror("(%s:%s:%d) execv failed for %s"
+ , __FILE__, __FUNCTION__, __LINE__, ra_pathname);
+
+ switch (errno) {
+ case ENOENT: /* No such file or directory */
+ case EISDIR: /* Is a directory */
+ exit_value = EXECRA_NOT_INSTALLED;
+ break;
+ default:
+ exit_value = EXECRA_EXEC_UNKNOWN_ERROR;
+ }
+ exit(exit_value);
+}
+
+static int
+prepare_cmd_parameters(const char * rsc_type, const char * op_type,
+ GHashTable * params_ht, RA_ARGV params_argv)
+{
+ int tmp_len, index;
+ int ht_size = 0;
+ int param_num = 0;
+ char buf_tmp[20];
+ void * value_tmp;
+
+ if (params_ht) {
+ ht_size = g_hash_table_size(params_ht);
+ }
+ if ( ht_size+3 > MAX_PARAMETER_NUM ) {
+ cl_log(LOG_ERR, "Too many parameters");
+ return -1;
+ }
+
+ /* Now suppose the parameter format stored in Hashtabe is as like as
+ * key="1", value="-Wl,soname=test"
+ * Moreover, the key is supposed as a string transfered from an integer.
+ * It may be changed in the future.
+ */
+ /* Notice: if ht_size==0, no actual arguments except op_type */
+ for (index = 1; index <= ht_size; index++ ) {
+ snprintf(buf_tmp, sizeof(buf_tmp), "%d", index);
+ value_tmp = g_hash_table_lookup(params_ht, buf_tmp);
+ /* suppose the key is consecutive */
+ if ( value_tmp == NULL ) {
+/* cl_log(LOG_WARNING, "Parameter ordering error in"\
+ "prepare_cmd_parameters, raexeclsb.c");
+ cl_log(LOG_WARNING, "search key=%s.", buf_tmp);
+*/ continue;
+ }
+ param_num ++;
+ params_argv[param_num] = g_strdup((char *)value_tmp);
+ }
+
+ tmp_len = strnlen(rsc_type, MAX_LENGTH_OF_RSCNAME);
+ params_argv[0] = g_strndup(rsc_type, tmp_len);
+ /* Add operation code as the last argument */
+ tmp_len = strnlen(op_type, MAX_LENGTH_OF_OPNAME);
+ params_argv[param_num+1] = g_strndup(op_type, tmp_len);
+ /* Add the teminating NULL pointer */
+ params_argv[param_num+2] = NULL;
+ return 0;
+}
+
+static uniform_ret_execra_t
+map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
+{
+
+ /* Now there is no formal related specification for Heartbeat RA
+ * scripts. Temporarily deal as LSB init script.
+ */
+ /* Except op_type equals 'status', the UNIFORM_RET_EXECRA is compatible
+ with LSB standard.
+ */
+ const char * stop_pattern1 = "*stopped*",
+ * stop_pattern2 = "*not*running*",
+ * running_pattern1 = "*running*",
+ * running_pattern2 = "*OK*";
+ char * lower_std_output = NULL;
+
+ if(ret_execra == EXECRA_NOT_INSTALLED) {
+ return ret_execra;
+ }
+
+ if ( 0 == STRNCMP_CONST(op_type, "status")
+ || 0 == STRNCMP_CONST(op_type, "monitor")) {
+ if (std_output == NULL ) {
+ cl_log(LOG_WARNING, "No status output from the (hb) resource agent.");
+ return EXECRA_NOT_RUNNING;
+ }
+
+ if (idebuglevel) {
+ cl_log(LOG_DEBUG, "RA output was: [%s]", std_output);
+ }
+
+ lower_std_output = g_ascii_strdown(std_output, -1);
+
+ if ( TRUE == g_pattern_match_simple(stop_pattern1
+ , lower_std_output) || TRUE ==
+ g_pattern_match_simple(stop_pattern2
+ , lower_std_output) ) {
+ if (idebuglevel) {
+ cl_log(LOG_DEBUG
+ , "RA output [%s] matched stopped pattern"
+ " [%s] or [%s]"
+ , std_output
+ , stop_pattern1
+ , stop_pattern2);
+ }
+ ret_execra = EXECRA_NOT_RUNNING; /* stopped */
+ } else if ( TRUE == g_pattern_match_simple(running_pattern1
+ , lower_std_output) || TRUE ==
+ g_pattern_match_simple(running_pattern2
+ , std_output) ) {
+ if (idebuglevel) {
+ cl_log(LOG_DEBUG
+ , "RA output [%s] matched running"
+ " pattern [%s] or [%s]"
+ , std_output, running_pattern1
+ , running_pattern2);
+ }
+ ret_execra = EXECRA_OK; /* running */
+ } else {
+ /* It didn't say it was running - must be stopped */
+ cl_log(LOG_DEBUG, "RA output [%s] didn't match any pattern"
+ , std_output);
+ ret_execra = EXECRA_NOT_RUNNING; /* stopped */
+ }
+ g_free(lower_std_output);
+ }
+ /* For non-status operation return code */
+ if (ret_execra < 0) {
+ ret_execra = EXECRA_UNKNOWN_ERROR;
+ }
+ return ret_execra;
+}
+
+static int
+get_resource_list(GList ** rsc_info)
+{
+ return get_runnable_list(RA_PATH, rsc_info);
+}
+
+static char*
+get_resource_meta(const char* rsc_type, const char* provider)
+{
+ GString * meta_data;
+
+ meta_data = g_string_new("");
+ g_string_sprintf( meta_data, meta_data_template, rsc_type
+ , rsc_type, rsc_type);
+ return meta_data->str;
+}
+static int
+get_provider_list(const char* ra_type, GList ** providers)
+{
+ if ( providers == NULL ) {
+ cl_log(LOG_ERR, "%s:%d: Parameter error: providers==NULL"
+ , __FUNCTION__, __LINE__);
+ return -2;
+ }
+
+ if ( *providers != NULL ) {
+ cl_log(LOG_ERR, "%s:%d: Parameter error: *providers==NULL."
+ "This will cause memory leak."
+ , __FUNCTION__, __LINE__);
+ }
+
+ /* Now temporarily make it fixed */
+ *providers = g_list_append(*providers, g_strdup("heartbeat"));
+
+ return g_list_length(*providers);
+}
diff --git a/lib/plugins/lrm/raexeclsb.c b/lib/plugins/lrm/raexeclsb.c
new file mode 100644
index 0000000..46d7546
--- /dev/null
+++ b/lib/plugins/lrm/raexeclsb.c
@@ -0,0 +1,609 @@
+/*
+ * 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.1 of the License, or (at your option) any later version.
+ *
+ * This software 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * File: raexeclsb.c
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This code implements the Resource Agent Plugin Module for LSB style.
+ * It's a part of Local Resource Manager. Currently it's used by lrmd only.
+ */
+/*
+ * Todo
+ * 1) Use flex&bison to make the analysis functions for lsb compliant comment?
+ * 2) Support multiple paths which contain lsb compliant RAs.
+ * 3) Optional and additional actions analysis?
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h> /* Add it for compiling on OSX */
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <pils/plugin.h>
+#include <lrm/raexec.h>
+#include <libgen.h>
+
+#include <libxml/entities.h>
+
+#define PIL_PLUGINTYPE RA_EXEC_TYPE
+#define PIL_PLUGIN lsb
+#define PIL_PLUGINTYPE_S "RAExec"
+#define PIL_PLUGIN_S "lsb"
+#define PIL_PLUGINLICENSE LICENSE_PUBDOM
+#define PIL_PLUGINLICENSEURL URL_PUBDOM
+
+
+/* meta-data template for lsb scripts */
+/* Note: As for optional actions -- extracted from lsb standard.
+ * The reload and the try-restart options are optional. Other init script
+ * actions may be defined by the init script.
+ */
+#define meta_data_template \
+"<?xml version=\"1.0\"?>\n"\
+"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"\
+"<resource-agent name=\"%s\" version=\"0.1\">\n"\
+" <version>1.0</version>\n"\
+" <longdesc lang=\"en\">\n"\
+" %s"\
+" </longdesc>\n"\
+" <shortdesc lang=\"en\">%s</shortdesc>\n"\
+" <parameters>\n"\
+" </parameters>\n"\
+" <actions>\n"\
+" <action name=\"start\" timeout=\"15\" />\n"\
+" <action name=\"stop\" timeout=\"15\" />\n"\
+" <action name=\"status\" timeout=\"15\" />\n"\
+" <action name=\"restart\" timeout=\"15\" />\n"\
+" <action name=\"force-reload\" timeout=\"15\" />\n"\
+" <action name=\"monitor\" timeout=\"15\" interval=\"15\" />\n"\
+" <action name=\"meta-data\" timeout=\"5\" />\n"\
+" </actions>\n"\
+" <special tag=\"LSB\">\n"\
+" <Provides>%s</Provides>\n"\
+" <Required-Start>%s</Required-Start>\n"\
+" <Required-Stop>%s</Required-Stop>\n"\
+" <Should-Start>%s</Should-Start>\n"\
+" <Should-Stop>%s</Should-Stop>\n"\
+" <Default-Start>%s</Default-Start>\n"\
+" <Default-Stop>%s</Default-Stop>\n"\
+" </special>\n"\
+"</resource-agent>\n"
+
+/* The keywords for lsb-compliant comment */
+#define LSB_INITSCRIPT_INFOBEGIN_TAG "### BEGIN INIT INFO"
+#define LSB_INITSCRIPT_INFOEND_TAG "### END INIT INFO"
+#define PROVIDES "# Provides:"
+#define REQ_START "# Required-Start:"
+#define REQ_STOP "# Required-Stop:"
+#define SHLD_START "# Should-Start:"
+#define SHLD_STOP "# Should-Stop:"
+#define DFLT_START "# Default-Start:"
+#define DFLT_STOP "# Default-Stop:"
+#define SHORT_DSCR "# Short-Description:"
+#define DESCRIPTION "# Description:"
+
+#define ZAPXMLOBJ(m) \
+ if ( (m) != NULL ) { \
+ xmlFree(m); \
+ (m) = NULL; \
+ }
+
+#define RALSB_GET_VALUE(ptr, keyword) \
+ if ( (ptr == NULL) & (0 == strncasecmp(buffer, keyword, strlen(keyword))) ) { \
+ (ptr) = (char *)xmlEncodeEntitiesReentrant(NULL,BAD_CAST buffer+strlen(keyword)); \
+ continue; \
+ }
+/*
+ * Are there multiple paths? Now according to LSB init scripts, the answer
+ * is 'no', but should be 'yes' for lsb none-init scripts?
+ */
+static const char * RA_PATH = LSB_RA_DIR;
+/* Map to the return code of the 'monitor' operation defined in the OCF RA
+ * specification.
+ */
+static const int status_op_exitcode_map[] = {
+ EXECRA_OK, /* LSB_STATUS_OK */
+ EXECRA_NOT_RUNNING, /* LSB_STATUS_VAR_PID */
+ EXECRA_NOT_RUNNING, /* LSB_STATUS_VAR_LOCK */
+ EXECRA_NOT_RUNNING, /* LSB_STATUS_STOPPED */
+ EXECRA_UNKNOWN_ERROR /* LSB_STATUS_UNKNOWN */
+};
+
+/* The begin of exported function list */
+static int execra(const char * rsc_id,
+ const char * rsc_type,
+ const char * provider,
+ const char * op_type,
+ const int timeout,
+ GHashTable * params);
+
+static uniform_ret_execra_t map_ra_retvalue(int ret_execra
+ , const char * op_type, const char * std_output);
+static char* get_resource_meta(const char* rsc_type, const char* provider);
+static int get_resource_list(GList ** rsc_info);
+static int get_provider_list(const char* ra_type, GList ** providers);
+
+/* The end of exported function list */
+
+/* The begin of internal used function & data list */
+#define MAX_PARAMETER_NUM 40
+
+const int MAX_LENGTH_OF_RSCNAME = 40,
+ MAX_LENGTH_OF_OPNAME = 40;
+
+typedef char * RA_ARGV[MAX_PARAMETER_NUM];
+
+static int prepare_cmd_parameters(const char * rsc_type, const char * op_type,
+ GHashTable * params, RA_ARGV params_argv);
+/* The end of internal function & data list */
+
+/* Rource agent execution plugin operations */
+static struct RAExecOps raops =
+{ execra,
+ map_ra_retvalue,
+ get_resource_list,
+ get_provider_list,
+ get_resource_meta
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static void* OurImports;
+static void* interfprivate;
+
+/*
+ * Our plugin initialization and registration function
+ * It gets called when the plugin gets loaded.
+ */
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interfaces */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S,
+ &raops, NULL, &OurInterface, &OurImports,
+ interfprivate);
+}
+
+/*
+ * Real work starts here ;-)
+ */
+
+static int
+execra( const char * rsc_id, const char * rsc_type, const char * provider,
+ const char * op_type, const int timeout, GHashTable * params)
+{
+ RA_ARGV params_argv;
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ GString * debug_info;
+ char * inherit_debuglevel = NULL;
+ char * optype_tmp = NULL;
+ int index_tmp = 0;
+ int save_errno;
+
+ /* Specially handle the operation "metameta-data". To build up its
+ * output from templet, dummy data and its comment head.
+ */
+ if ( 0 == STRNCMP_CONST(op_type, "meta-data")) {
+ printf("%s", get_resource_meta(rsc_type, provider));
+ exit(0);
+ }
+
+ /* To simulate the 'monitor' operation with 'status'.
+ * Now suppose there is no 'monitor' operation for LSB scripts.
+ */
+ if (0 == STRNCMP_CONST(op_type, "monitor")) {
+ optype_tmp = g_strdup("status");
+ } else {
+ optype_tmp = g_strdup(op_type);
+ }
+
+ /* Prepare the call parameter */
+ if ( prepare_cmd_parameters(rsc_type, optype_tmp, params, params_argv)
+ != 0) {
+ cl_log(LOG_ERR, "lsb RA: Error of preparing parameters");
+ g_free(optype_tmp);
+ return -1;
+ }
+ g_free(optype_tmp);
+ get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname);
+
+ /* let this log show only high loglevel. */
+ inherit_debuglevel = getenv(HADEBUGVAL);
+ if ((inherit_debuglevel != NULL) && (atoi(inherit_debuglevel) > 1)) {
+ debug_info = g_string_new("");
+ do {
+ g_string_append(debug_info, params_argv[index_tmp]);
+ g_string_append(debug_info, " ");
+ } while (params_argv[++index_tmp] != NULL);
+ debug_info->str[debug_info->len-1] = '\0';
+
+ cl_log(LOG_DEBUG, "RA instance %s executing: lsb::%s"
+ , rsc_id, debug_info->str);
+
+ g_string_free(debug_info, TRUE);
+ }
+
+ closefiles(); /* don't leak open files */
+ execv(ra_pathname, params_argv);
+ /* oops, exec failed */
+ save_errno = errno; /* cl_perror may change errno */
+ cl_perror("(%s:%s:%d) execv failed for %s"
+ , __FILE__, __FUNCTION__, __LINE__, ra_pathname);
+ errno = save_errno;
+ exit(get_failed_exec_rc());
+}
+
+static uniform_ret_execra_t
+map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
+{
+ /* Except op_type equals 'status', the UNIFORM_RET_EXECRA is compatible
+ * with the LSB standard.
+ */
+ if (ret_execra < 0) {
+ return EXECRA_UNKNOWN_ERROR;
+ }
+
+ if(ret_execra == EXECRA_NOT_INSTALLED) {
+ return ret_execra;
+ }
+
+ if ( 0 == STRNCMP_CONST(op_type, "status")
+ || 0 == STRNCMP_CONST(op_type, "monitor")) {
+ if (ret_execra < DIMOF(status_op_exitcode_map)) {
+ ret_execra = status_op_exitcode_map[ret_execra];
+ }
+ }
+ return ret_execra;
+}
+
+static int
+get_resource_list(GList ** rsc_info)
+{
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ FILE * fp;
+ gboolean next_continue, found_begin_tag, is_lsb_script;
+ int rc = 0;
+ GList *cur, *tmp;
+ const size_t BUFLEN = 80;
+ char buffer[BUFLEN];
+
+ if ((rc = get_runnable_list(RA_PATH, rsc_info)) <= 0) {
+ return rc;
+ }
+
+ /* Use the following comment line as the filter patterns to choose
+ * the real LSB-compliant scripts.
+ * "### BEGIN INIT INFO" and "### END INIT INFO"
+ */
+ cur = g_list_first(*rsc_info);
+ while ( cur != NULL ) {
+ get_ra_pathname(RA_PATH, cur->data, NULL, ra_pathname);
+ if ( (fp = fopen(ra_pathname, "r")) == NULL ) {
+ tmp = g_list_next(cur);
+ *rsc_info = g_list_remove(*rsc_info, cur->data);
+ if (cur->data)
+ g_free(cur->data);
+ cur = tmp;
+ continue;
+ }
+ is_lsb_script = FALSE;
+ next_continue = FALSE;
+ found_begin_tag = FALSE;
+ while (NULL != fgets(buffer, BUFLEN, fp)) {
+ /* Handle the lines over BUFLEN(80) columns, only
+ * the first part is compared.
+ */
+ if ( next_continue == TRUE ) {
+ continue;
+ }
+ if (strlen(buffer) == BUFLEN ) {
+ next_continue = TRUE;
+ } else {
+ next_continue = FALSE;
+ }
+ /* Shorten the search time */
+ if (buffer[0] != '#' && buffer[0] != ' '
+ && buffer[0] != '\n') {
+ break; /* donnot find */
+ }
+
+ if (found_begin_tag == TRUE && 0 == strncasecmp(buffer
+ , LSB_INITSCRIPT_INFOEND_TAG
+ , strlen(LSB_INITSCRIPT_INFOEND_TAG)) ) {
+ is_lsb_script = TRUE;
+ break;
+ }
+ if (found_begin_tag == FALSE && 0 == strncasecmp(buffer
+ , LSB_INITSCRIPT_INFOBEGIN_TAG
+ , strlen(LSB_INITSCRIPT_INFOBEGIN_TAG)) ) {
+ found_begin_tag = TRUE;
+ }
+ }
+ fclose(fp);
+ tmp = g_list_next(cur);
+
+/*
+ * Temporarily remove the filter to the initscript, or many initscripts on
+ * many distros, such as RHEL4 and fedora5, cannot be used by management GUI.
+ * Please refer to the bug
+ * http://www.osdl.org/developer_bugzilla/show_bug.cgi?id=1250
+ */
+
+#if 0
+ if ( is_lsb_script != TRUE ) {
+ *rsc_info = g_list_remove(*rsc_info, cur->data);
+ g_free(cur->data);
+ }
+#else
+ (void) is_lsb_script;
+#endif
+ cur = tmp;
+ }
+
+ return g_list_length(*rsc_info);
+}
+
+static int
+prepare_cmd_parameters(const char * rsc_type, const char * op_type,
+ GHashTable * params_ht, RA_ARGV params_argv)
+{
+ int tmp_len;
+ int ht_size = 0;
+#if 0
+ /* Reserve it for possible furture use */
+ int index;
+ void * value_tmp = NULL;
+ char buf_tmp[20];
+#endif
+
+ if (params_ht) {
+ ht_size = g_hash_table_size(params_ht);
+ }
+
+ /* Need 3 additonal spaces for accomodating:
+ * argv[0] = RA_file_name(RA_TYPE)
+ * argv[1] = operation
+ * a terminal NULL
+ */
+ if ( ht_size+3 > MAX_PARAMETER_NUM ) {
+ cl_log(LOG_ERR, "Too many parameters");
+ return -1;
+ }
+
+ tmp_len = strnlen(rsc_type, MAX_LENGTH_OF_RSCNAME);
+ params_argv[0] = g_strndup(rsc_type, tmp_len);
+ /* Add operation code as the first argument */
+ tmp_len = strnlen(op_type, MAX_LENGTH_OF_OPNAME);
+ params_argv[1] = g_strndup(op_type, tmp_len);
+
+ /*
+ * No actual arguments needed except op_type.
+ * Add the teminating NULL pointer.
+ */
+ params_argv[2] = NULL;
+ if ( (ht_size != 0) && (0 != STRNCMP_CONST(op_type, "status")) ) {
+ cl_log(LOG_WARNING, "For LSB init script, no additional "
+ "parameters are needed.");
+ }
+
+/* Actually comment the following code, but I still think it may be used
+ * in the future for LSB none-initial scripts, so reserver it.
+ */
+#if 0
+ /* Now suppose the parameter formate stored in Hashtabe is like
+ * key="1", value="-Wl,soname=test"
+ * Moreover, the key is supposed as a string transfered from an integer.
+ * It may be changed in the future.
+ */
+ for (index = 1; index <= ht_size; index++ ) {
+ snprintf(buf_tmp, sizeof(buf_tmp), "%d", index);
+ value_tmp = g_hash_table_lookup(params_ht, buf_tmp);
+ /* suppose the key is consecutive */
+ if ( value_tmp == NULL ) {
+ cl_log(LOG_ERR, "Parameter ordering error in"\
+ "prepare_cmd_parameters, raexeclsb.c");
+ return -1;
+ }
+ params_argv[index+1] = g_strdup((char *)value_tmp);
+ }
+#endif
+
+ return 0;
+}
+
+static char*
+get_resource_meta(const char* rsc_type, const char* provider)
+{
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ FILE * fp;
+ gboolean next_continue;
+ GString * meta_data;
+ const size_t BUFLEN = 132;
+ char buffer[BUFLEN];
+ char * provides = NULL,
+ * req_start = NULL,
+ * req_stop = NULL,
+ * shld_start = NULL,
+ * shld_stop = NULL,
+ * dflt_start = NULL,
+ * dflt_stop = NULL,
+ * s_dscrpt = NULL,
+ * xml_l_dscrpt = NULL;
+ GString * l_dscrpt = NULL;
+
+ /*
+ * Use the following tags to find the LSb-compliant comment block.
+ * "### BEGIN INIT INFO" and "### END INIT INFO"
+ */
+ get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname);
+ if ( (fp = fopen(ra_pathname, "r")) == NULL ) {
+ cl_log(LOG_ERR, "Failed to open lsb RA %s. No meta-data gotten."
+ , rsc_type);
+ return NULL;
+ }
+ meta_data = g_string_new("");
+
+ next_continue = FALSE;
+
+/*
+ * Is not stick to the rule that the description should be located in the
+ * comment block between "### BEGIN INIT INFO" and "### END INIT INFO".
+ * Please refer to the bug
+ * http://www.osdl.org/developer_bugzilla/show_bug.cgi?id=1250
+ */
+#if 0
+ while (NULL != fgets(buffer, BUFLEN, fp)) {
+ /* Handle the lines over BUFLEN(80) columns, only
+ * the first part is compared.
+ */
+ if ( next_continue == TRUE ) {
+ continue;
+ }
+ if (strlen(buffer) == BUFLEN ) {
+ next_continue = TRUE;
+ } else {
+ next_continue = FALSE;
+ }
+
+ if ( 0 == strncasecmp(buffer , LSB_INITSCRIPT_INFOBEGIN_TAG
+ , strlen(LSB_INITSCRIPT_INFOBEGIN_TAG)) ) {
+ break;
+ }
+ }
+#else
+ (void) next_continue;
+#endif
+
+ /* Enter into the lsb-compliant comment block */
+ while ( NULL != fgets(buffer, BUFLEN, fp) ) {
+ /* Now suppose each of the following eight arguments contain
+ * only one line
+ */
+ RALSB_GET_VALUE(provides, PROVIDES)
+ RALSB_GET_VALUE(req_start, REQ_START)
+ RALSB_GET_VALUE(req_stop, REQ_STOP)
+ RALSB_GET_VALUE(shld_start, SHLD_START)
+ RALSB_GET_VALUE(shld_stop, SHLD_STOP)
+ RALSB_GET_VALUE(dflt_start, DFLT_START)
+ RALSB_GET_VALUE(dflt_stop, DFLT_STOP)
+ RALSB_GET_VALUE(s_dscrpt, SHORT_DSCR)
+
+ /* Long description may cross multiple lines */
+ if ( (l_dscrpt == NULL) && (0 == strncasecmp(buffer, DESCRIPTION
+ , strlen(DESCRIPTION))) ) {
+ l_dscrpt = g_string_new(buffer+strlen(DESCRIPTION));
+ /* Between # and keyword, more than one space, or a tab
+ * character, indicates the continuation line.
+ * Extracted from LSB init script standard
+ */
+ while ( NULL != fgets(buffer, BUFLEN, fp) ) {
+ if ( (0 == strncmp(buffer, "# ", 3))
+ || (0 == strncmp(buffer, "#\t", 2)) ) {
+ buffer[0] = ' ';
+ l_dscrpt = g_string_append(l_dscrpt
+ , buffer);
+ } else {
+ fputs(buffer, fp);
+ break; /* Long description ends */
+ }
+ }
+ continue;
+ }
+ if( l_dscrpt )
+ xml_l_dscrpt = (char *)xmlEncodeEntitiesReentrant(NULL,
+ BAD_CAST (l_dscrpt->str));
+
+ if ( 0 == strncasecmp(buffer, LSB_INITSCRIPT_INFOEND_TAG
+ , strlen(LSB_INITSCRIPT_INFOEND_TAG)) ) {
+ /* Get to the out border of LSB comment block */
+ break;
+ }
+
+ if ( buffer[0] != '#' ) {
+ break; /* Out of comment block in the beginning */
+ }
+ }
+ fclose(fp);
+
+ g_string_sprintf( meta_data, meta_data_template, rsc_type
+ , (xml_l_dscrpt==NULL)? rsc_type : xml_l_dscrpt
+ , (s_dscrpt==NULL)? rsc_type : s_dscrpt
+ , (provides==NULL)? "" : provides
+ , (req_start==NULL)? "" : req_start
+ , (req_stop==NULL)? "" : req_stop
+ , (shld_start==NULL)? "" : shld_start
+ , (shld_stop==NULL)? "" : shld_stop
+ , (dflt_start==NULL)? "" : dflt_start
+ , (dflt_stop==NULL)? "" : dflt_stop );
+
+ ZAPXMLOBJ(xml_l_dscrpt);
+ ZAPXMLOBJ(s_dscrpt);
+ ZAPXMLOBJ(provides);
+ ZAPXMLOBJ(req_start);
+ ZAPXMLOBJ(req_stop);
+ ZAPXMLOBJ(shld_start);
+ ZAPXMLOBJ(shld_stop);
+ ZAPXMLOBJ(dflt_start);
+ ZAPXMLOBJ(dflt_stop);
+
+ if( l_dscrpt )
+ g_string_free(l_dscrpt, TRUE);
+ return meta_data->str;
+}
+
+static int
+get_provider_list(const char* ra_type, GList ** providers)
+{
+ if ( providers == NULL ) {
+ cl_log(LOG_ERR, "%s:%d: Parameter error: providers==NULL"
+ , __FUNCTION__, __LINE__);
+ return -2;
+ }
+
+ if ( *providers != NULL ) {
+ cl_log(LOG_ERR, "%s:%d: Parameter error: *providers==NULL."
+ "This will cause memory leak."
+ , __FUNCTION__, __LINE__);
+ }
+
+ /* Now temporarily make it fixed */
+ *providers = g_list_append(*providers, g_strdup("heartbeat"));
+
+ return g_list_length(*providers);
+}
diff --git a/lib/plugins/lrm/raexecocf.c b/lib/plugins/lrm/raexecocf.c
new file mode 100644
index 0000000..f7cd7ed
--- /dev/null
+++ b/lib/plugins/lrm/raexecocf.c
@@ -0,0 +1,496 @@
+/*
+ * 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.1 of the License, or (at your option) any later version.
+ *
+ * This software 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * File: raexecocf.c
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This code implements the Resource Agent Plugin Module for LSB style.
+ * It's a part of Local Resource Manager. Currently it's used by lrmd only.
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/realtime.h>
+#include <pils/plugin.h>
+#include <dirent.h>
+#include <libgen.h> /* Add it for compiling on OSX */
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include <lrm/raexec.h>
+
+# define PIL_PLUGINTYPE RA_EXEC_TYPE
+# define PIL_PLUGINTYPE_S "RAExec"
+# define PIL_PLUGINLICENSE LICENSE_PUBDOM
+# define PIL_PLUGINLICENSEURL URL_PUBDOM
+
+# define PIL_PLUGIN ocf
+# define PIL_PLUGIN_S "ocf"
+/*
+ * Are there multiple paths? Now according to OCF spec, the answer is 'no'.
+ * But actually or for future?
+ */
+static const char * RA_PATH = OCF_RA_DIR;
+
+/* The begin of exported function list */
+static int execra(const char * rsc_id,
+ const char * rsc_type,
+ const char * provider,
+ const char * op_type,
+ const int timeout,
+ GHashTable * params);
+static uniform_ret_execra_t map_ra_retvalue(int ret_execra,
+ const char * op_type, const char * std_output);
+static int get_resource_list(GList ** rsc_info);
+static char* get_resource_meta(const char* rsc_type, const char* provider);
+static int get_provider_list(const char* ra_type, GList ** providers);
+
+/* The end of exported function list */
+
+/* The begin of internal used function & data list */
+static void add_OCF_prefix(GHashTable * params, GHashTable * new_params);
+static void add_OCF_env_vars(GHashTable * env, const char * rsc_id,
+ const char * rsc_type, const char * provider);
+static void add_prefix_foreach(gpointer key, gpointer value,
+ gpointer user_data);
+
+static void hash_to_str(GHashTable * , GString *);
+static void hash_to_str_foreach(gpointer key, gpointer value,
+ gpointer user_data);
+
+static int raexec_setenv(GHashTable * env_params);
+static void set_env(gpointer key, gpointer value, gpointer user_data);
+
+static gboolean let_remove_eachitem(gpointer key, gpointer value,
+ gpointer user_data);
+static int get_providers(const char* class_path, const char* op_type,
+ GList ** providers);
+static void merge_string_list(GList** old, GList* new);
+static gint compare_str(gconstpointer a, gconstpointer b);
+
+/* The end of internal function & data list */
+
+/* Rource agent execution plugin operations */
+static struct RAExecOps raops =
+{ execra,
+ map_ra_retvalue,
+ get_resource_list,
+ get_provider_list,
+ get_resource_meta
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static void* OurImports;
+static void* interfprivate;
+
+/*
+ * Our plugin initialization and registration function
+ * It gets called when the plugin gets loaded.
+ */
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interfaces */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S,
+ &raops, NULL, &OurInterface, &OurImports,
+ interfprivate);
+}
+
+/*
+ * The function to execute a RA.
+ */
+static int
+execra(const char * rsc_id, const char * rsc_type, const char * provider,
+ const char * op_type, const int timeout, GHashTable * params)
+{
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ GHashTable * tmp_for_setenv;
+ GString * params_gstring;
+ char * inherit_debuglevel = NULL;
+ int save_errno;
+
+ get_ra_pathname(RA_PATH, rsc_type, provider, ra_pathname);
+
+ /* Setup environment correctly */
+ tmp_for_setenv = g_hash_table_new(g_str_hash, g_str_equal);
+ add_OCF_prefix(params, tmp_for_setenv);
+ add_OCF_env_vars(tmp_for_setenv, rsc_id, rsc_type, provider);
+ raexec_setenv(tmp_for_setenv);
+ g_hash_table_foreach_remove(tmp_for_setenv, let_remove_eachitem, NULL);
+ g_hash_table_destroy(tmp_for_setenv);
+
+ /* let this log show only high loglevel. */
+ inherit_debuglevel = getenv(HADEBUGVAL);
+ if ((inherit_debuglevel != NULL) && (atoi(inherit_debuglevel) > 1)) {
+ params_gstring = g_string_new("");
+ hash_to_str(params, params_gstring);
+ cl_log(LOG_DEBUG, "RA instance %s executing: OCF::%s %s. Parameters: "
+ "{%s}", rsc_id, rsc_type, op_type, params_gstring->str);
+ g_string_free(params_gstring, TRUE);
+ }
+
+ closefiles(); /* don't leak open files */
+ /* execute the RA */
+ execl(ra_pathname, ra_pathname, op_type, (const char *)NULL);
+ /* oops, exec failed */
+ save_errno = errno; /* cl_perror may change errno */
+ cl_perror("(%s:%s:%d) execl failed for %s"
+ , __FILE__, __FUNCTION__, __LINE__, ra_pathname);
+ errno = save_errno;
+ exit(get_failed_exec_rc());
+}
+
+static uniform_ret_execra_t
+map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
+{
+ /* Because the UNIFORM_RET_EXECRA is compatible with OCF standard,
+ * no actual mapping except validating, which ensure the return code
+ * will be in the range 0 to 7. Too strict?
+ */
+ if (ret_execra < 0 || ret_execra > 9) {
+ cl_log(LOG_WARNING, "mapped the invalid return code %d."
+ , ret_execra);
+ ret_execra = EXECRA_UNKNOWN_ERROR;
+ }
+ return ret_execra;
+}
+
+static gint
+compare_str(gconstpointer a, gconstpointer b)
+{
+ return strncmp(a,b,RA_MAX_NAME_LENGTH);
+}
+
+static int
+get_resource_list(GList ** rsc_info)
+{
+ struct dirent **namelist;
+ GList* item;
+ int file_num;
+ char subdir[FILENAME_MAX+1];
+
+ if ( rsc_info == NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_resource_list");
+ return -2;
+ }
+
+ if ( *rsc_info != NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_resource_list."\
+ "will cause memory leak.");
+ *rsc_info = NULL;
+ }
+ file_num = scandir(RA_PATH, &namelist, NULL, alphasort);
+ if (file_num < 0) {
+ return -2;
+ }
+ while (file_num--) {
+ GList* ra_subdir = NULL;
+ struct stat prop;
+ if ('.' == namelist[file_num]->d_name[0]) {
+ free(namelist[file_num]);
+ continue;
+ }
+
+ snprintf(subdir,FILENAME_MAX,"%s/%s",
+ RA_PATH, namelist[file_num]->d_name);
+
+ if (stat(subdir, &prop) == -1) {
+ cl_perror("%s:%s:%d: stat failed for %s"
+ , __FILE__, __FUNCTION__, __LINE__, subdir);
+ free(namelist[file_num]);
+ continue;
+ } else if (!S_ISDIR(prop.st_mode)) {
+ free(namelist[file_num]);
+ continue;
+ }
+
+ get_runnable_list(subdir,&ra_subdir);
+
+ merge_string_list(rsc_info,ra_subdir);
+
+ while (NULL != (item = g_list_first(ra_subdir))) {
+ ra_subdir = g_list_remove_link(ra_subdir, item);
+ g_free(item->data);
+ g_list_free_1(item);
+ }
+
+ free(namelist[file_num]);
+ }
+ free(namelist);
+
+ return 0;
+}
+
+static void
+merge_string_list(GList** old, GList* new)
+{
+ GList* item = NULL;
+ char* newitem;
+ for( item=g_list_first(new); NULL!=item; item=g_list_next(item)){
+ if (!g_list_find_custom(*old, item->data,compare_str)){
+ newitem = g_strndup(item->data,RA_MAX_NAME_LENGTH);
+ *old = g_list_append(*old, newitem);
+ }
+ }
+}
+
+static int
+get_provider_list(const char* ra_type, GList ** providers)
+{
+ int ret;
+ ret = get_providers(RA_PATH, ra_type, providers);
+ if (0>ret) {
+ cl_log(LOG_ERR, "scandir failed in OCF RA plugin");
+ }
+ return ret;
+}
+
+static char*
+get_resource_meta(const char* rsc_type, const char* provider)
+{
+ const int BUFF_LEN=4096;
+ int read_len = 0;
+ char buff[BUFF_LEN];
+ char* data = NULL;
+ GString* g_str_tmp = NULL;
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ FILE* file = NULL;
+ GHashTable * tmp_for_setenv;
+ struct timespec short_sleep = {0,200000000L}; /*20ms*/
+
+ get_ra_pathname(RA_PATH, rsc_type, provider, ra_pathname);
+
+ strncat(ra_pathname, " meta-data",RA_MAX_NAME_LENGTH-strlen(ra_pathname)-1);
+ tmp_for_setenv = g_hash_table_new(g_str_hash, g_str_equal);
+ add_OCF_env_vars(tmp_for_setenv, "DUMMY_INSTANCE", rsc_type, provider);
+ raexec_setenv(tmp_for_setenv);
+ g_hash_table_foreach_remove(tmp_for_setenv, let_remove_eachitem, NULL);
+ g_hash_table_destroy(tmp_for_setenv);
+
+ file = popen(ra_pathname, "r");
+ if (NULL==file) {
+ cl_log(LOG_ERR, "%s: popen failed: %s", __FUNCTION__, strerror(errno));
+ return NULL;
+ }
+
+ g_str_tmp = g_string_new("");
+ while(!feof(file)) {
+ read_len = fread(buff, 1, BUFF_LEN - 1, file);
+ if (0<read_len) {
+ *(buff+read_len) = '\0';
+ g_string_append(g_str_tmp, buff);
+ }
+ else {
+ nanosleep(&short_sleep,NULL);
+ }
+ }
+ if( pclose(file) ) {
+ cl_log(LOG_ERR, "%s: pclose failed: %s", __FUNCTION__, strerror(errno));
+ }
+ if (0 == g_str_tmp->len) {
+ g_string_free(g_str_tmp, TRUE);
+ return NULL;
+ }
+ data = (char*)g_new(char, g_str_tmp->len+1);
+ data[0] = data[g_str_tmp->len] = 0;
+ strncpy(data, g_str_tmp->str, g_str_tmp->len);
+
+ g_string_free(g_str_tmp, TRUE);
+
+ return data;
+}
+
+static void
+add_OCF_prefix(GHashTable * env_params, GHashTable * new_env_params)
+{
+ if (env_params) {
+ g_hash_table_foreach(env_params, add_prefix_foreach,
+ new_env_params);
+ }
+}
+
+static void
+add_prefix_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+ const int MAX_LENGTH_OF_ENV = 128;
+ int prefix = STRLEN_CONST("OCF_RESKEY_");
+ GHashTable * new_hashtable = (GHashTable *) user_data;
+ char * newkey;
+ int keylen = strnlen((char*)key, MAX_LENGTH_OF_ENV-prefix)+prefix+1;
+
+ newkey = g_new(gchar, keylen);
+ strncpy(newkey, "OCF_RESKEY_", keylen);
+ strncat(newkey, key, keylen-strlen(newkey)-1);
+ g_hash_table_insert(new_hashtable, (gpointer)newkey, g_strdup(value));
+}
+
+static void
+hash_to_str(GHashTable * params , GString * str)
+{
+ if (params) {
+ g_hash_table_foreach(params, hash_to_str_foreach, str);
+ }
+}
+
+static void
+hash_to_str_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+ char buffer_tmp[60];
+ GString * str = (GString *)user_data;
+
+ snprintf(buffer_tmp, 60, "%s=%s ", (char *)key, (char *)value);
+ str = g_string_append(str, buffer_tmp);
+}
+
+static gboolean
+let_remove_eachitem(gpointer key, gpointer value, gpointer user_data)
+{
+ g_free(key);
+ g_free(value);
+ return TRUE;
+}
+
+static int
+raexec_setenv(GHashTable * env_params)
+{
+ if (env_params) {
+ g_hash_table_foreach(env_params, set_env, NULL);
+ }
+ return 0;
+}
+
+static void
+set_env(gpointer key, gpointer value, gpointer user_data)
+{
+ if (setenv(key, value, 1) != 0) {
+ cl_log(LOG_ERR, "setenv failed in raexecocf.");
+ }
+}
+
+static int
+get_providers(const char* class_path, const char* ra_type, GList ** providers)
+{
+ struct dirent **namelist;
+ int file_num;
+
+ if ( providers == NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_providers");
+ return -2;
+ }
+
+ if ( *providers != NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_providers."\
+ "will cause memory leak.");
+ *providers = NULL;
+ }
+
+ file_num = scandir(class_path, &namelist, 0, alphasort);
+ if (file_num < 0) {
+ return -2;
+ }else{
+ char tmp_buffer[FILENAME_MAX+1];
+ struct stat prop;
+
+ while (file_num--) {
+ if ('.' == namelist[file_num]->d_name[0]) {
+ free(namelist[file_num]);
+ continue;
+ }
+ snprintf(tmp_buffer,FILENAME_MAX,"%s/%s",
+ class_path, namelist[file_num]->d_name);
+ stat(tmp_buffer, &prop);
+ if (!S_ISDIR(prop.st_mode)) {
+ free(namelist[file_num]);
+ continue;
+ }
+
+ snprintf(tmp_buffer,FILENAME_MAX,"%s/%s/%s",
+ class_path, namelist[file_num]->d_name, ra_type);
+
+ if ( filtered(tmp_buffer) == TRUE ) {
+ *providers = g_list_append(*providers,
+ g_strdup(namelist[file_num]->d_name));
+ }
+ free(namelist[file_num]);
+ }
+ free(namelist);
+ }
+ return g_list_length(*providers);
+}
+
+static void
+add_OCF_env_vars(GHashTable * env, const char * rsc_id,
+ const char * rsc_type, const char * provider)
+{
+ if ( env == NULL ) {
+ cl_log(LOG_WARNING, "env should not be a NULL pointer.");
+ return;
+ }
+
+ g_hash_table_insert(env, g_strdup("OCF_RA_VERSION_MAJOR"),
+ g_strdup("1"));
+ g_hash_table_insert(env, g_strdup("OCF_RA_VERSION_MINOR"),
+ g_strdup("0"));
+ g_hash_table_insert(env, g_strdup("OCF_ROOT"),
+ g_strdup(OCF_ROOT_DIR));
+
+ if ( rsc_id != NULL ) {
+ g_hash_table_insert(env, g_strdup("OCF_RESOURCE_INSTANCE"),
+ g_strdup(rsc_id));
+ }
+
+ /* Currently the rsc_type=="the filename of the RA script/executable",
+ * It seems always correct even in the furture. ;-)
+ */
+ if ( rsc_type != NULL ) {
+ g_hash_table_insert(env, g_strdup("OCF_RESOURCE_TYPE"),
+ g_strdup(rsc_type));
+ }
+
+ /* Notes: this is not added to specification yet. Sept 10,2004 */
+ if ( provider != NULL ) {
+ g_hash_table_insert(env, g_strdup("OCF_RESOURCE_PROVIDER"),
+ g_strdup(provider));
+ }
+}
+
diff --git a/lib/plugins/lrm/raexecupstart.c b/lib/plugins/lrm/raexecupstart.c
new file mode 100644
index 0000000..baa0278
--- /dev/null
+++ b/lib/plugins/lrm/raexecupstart.c
@@ -0,0 +1,222 @@
+/*
+ * 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.1 of the License, or (at your option) any later version.
+ *
+ * This software 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * File: raexecupstart.c
+ * Copyright (C) 2010 Senko Rasic <senko.rasic@dobarkod.hr>
+ * Copyright (c) 2010 Ante Karamatic <ivoks@init.hr>
+ *
+ * Heavily based on raexeclsb.c and raexechb.c:
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This code implements the Resource Agent Plugin Module for Upstart.
+ * It's a part of Local Resource Manager. Currently it's used by lrmd only.
+ */
+
+#define PIL_PLUGINTYPE RA_EXEC_TYPE
+#define PIL_PLUGIN upstart
+#define PIL_PLUGINTYPE_S "RAExec"
+#define PIL_PLUGIN_S "upstart"
+#define PIL_PLUGINLICENSE LICENSE_PUBDOM
+#define PIL_PLUGINLICENSEURL URL_PUBDOM
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h> /* Add it for compiling on OSX */
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <pils/plugin.h>
+#include <lrm/raexec.h>
+#include <libgen.h>
+
+#include <glib-object.h>
+
+#include <libxml/entities.h>
+
+#include "upstart-dbus.h"
+
+#define meta_data_template \
+"<?xml version=\"1.0\"?>\n"\
+"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"\
+"<resource-agent name=\"%s\" version=\"0.1\">\n"\
+" <version>1.0</version>\n"\
+" <longdesc lang=\"en\">\n"\
+" %s"\
+" </longdesc>\n"\
+" <shortdesc lang=\"en\">%s</shortdesc>\n"\
+" <parameters>\n"\
+" </parameters>\n"\
+" <actions>\n"\
+" <action name=\"start\" timeout=\"15\" />\n"\
+" <action name=\"stop\" timeout=\"15\" />\n"\
+" <action name=\"status\" timeout=\"15\" />\n"\
+" <action name=\"restart\" timeout=\"15\" />\n"\
+" <action name=\"monitor\" timeout=\"15\" interval=\"15\" start-delay=\"15\" />\n"\
+" <action name=\"meta-data\" timeout=\"5\" />\n"\
+" </actions>\n"\
+" <special tag=\"upstart\">\n"\
+" </special>\n"\
+"</resource-agent>\n"
+
+/* The begin of exported function list */
+static int execra(const char * rsc_id,
+ const char * rsc_type,
+ const char * provider,
+ const char * op_type,
+ const int timeout,
+ GHashTable * params);
+
+static uniform_ret_execra_t map_ra_retvalue(int ret_execra
+ , const char * op_type, const char * std_output);
+static char* get_resource_meta(const char* rsc_type, const char* provider);
+static int get_resource_list(GList ** rsc_info);
+static int get_provider_list(const char* ra_type, GList ** providers);
+
+/* The end of exported function list */
+
+/* The begin of internal used function & data list */
+#define MAX_PARAMETER_NUM 40
+
+const int MAX_LENGTH_OF_RSCNAME = 40,
+ MAX_LENGTH_OF_OPNAME = 40;
+
+typedef char * RA_ARGV[MAX_PARAMETER_NUM];
+
+/* The end of internal function & data list */
+
+/* Rource agent execution plugin operations */
+static struct RAExecOps raops =
+{ execra,
+ map_ra_retvalue,
+ get_resource_list,
+ get_provider_list,
+ get_resource_meta
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static void* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
+{
+ PluginImports = imports;
+ OurPlugin = us;
+
+ imports->register_plugin(us, &OurPIExports);
+
+ g_type_init ();
+
+ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S,
+ &raops, NULL, &OurInterface, &OurImports,
+ interfprivate);
+}
+
+static int
+execra( const char * rsc_id, const char * rsc_type, const char * provider,
+ const char * op_type, const int timeout, GHashTable * params)
+{
+ UpstartJobCommand cmd;
+
+ if (!g_strcmp0(op_type, "meta-data")) {
+ printf("%s", get_resource_meta(rsc_type, provider));
+ exit(EXECRA_OK);
+ } else if (!g_strcmp0(op_type, "monitor") || !g_strcmp0(op_type, "status")) {
+ gboolean running = upstart_job_is_running (rsc_type);
+ printf("%s", running ? "running" : "stopped");
+
+ if (running)
+ exit(EXECRA_OK);
+ else
+ exit(EXECRA_NOT_RUNNING);
+ } else if (!g_strcmp0(op_type, "start")) {
+ cmd = UPSTART_JOB_START;
+ } else if (!g_strcmp0(op_type, "stop")) {
+ cmd = UPSTART_JOB_STOP;
+ } else if (!g_strcmp0(op_type, "restart")) {
+ cmd = UPSTART_JOB_RESTART;
+ } else {
+ exit(EXECRA_UNIMPLEMENT_FEATURE);
+ }
+
+ /* It'd be better if it returned GError, so we can distinguish
+ * between failure modes (can't contact upstart, no such job,
+ * or failure to do action. */
+ if (upstart_job_do(rsc_type, cmd, timeout)) {
+ exit(EXECRA_OK);
+ } else {
+ exit(EXECRA_NO_RA);
+ }
+}
+
+static uniform_ret_execra_t
+map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
+{
+ /* no need to map anything, execra() returns correct exit code */
+ return ret_execra;
+}
+
+static int
+get_resource_list(GList ** rsc_info)
+{
+ gchar **jobs;
+ gint i;
+ *rsc_info = NULL;
+
+ jobs = upstart_get_all_jobs();
+
+ if (!jobs)
+ return 0;
+
+ for (i = 0; jobs[i] != NULL; i++) {
+ *rsc_info = g_list_prepend(*rsc_info, jobs[i]);
+ }
+
+ /* free the array, but not the strings */
+ g_free(jobs);
+
+ *rsc_info = g_list_reverse(*rsc_info);
+ return g_list_length(*rsc_info);
+}
+
+static char *
+get_resource_meta (const gchar *rsc_type, const gchar *provider)
+{
+ return g_strdup_printf(meta_data_template, rsc_type,
+ rsc_type, rsc_type);
+}
+
+static int
+get_provider_list (const gchar *ra_type, GList **providers)
+{
+ *providers = g_list_prepend(*providers, g_strdup("upstart"));
+ return g_list_length(*providers);
+}
+
diff --git a/lib/plugins/lrm/upstart-dbus.c b/lib/plugins/lrm/upstart-dbus.c
new file mode 100644
index 0000000..b994d87
--- /dev/null
+++ b/lib/plugins/lrm/upstart-dbus.c
@@ -0,0 +1,406 @@
+/*
+ * 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.1 of the License, or (at your option) any later version.
+ *
+ * This software 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * File: upstart-dbus.c
+ * Copyright (C) 2010 Senko Rasic <senko.rasic@dobarkod.hr>
+ * Copyright (c) 2010 Ante Karamatic <ivoks@init.hr>
+ *
+ *
+ * Each exported function is standalone, and creates a new connection to
+ * the upstart daemon. This is because lrmd plugins fork off for exec,
+ * and if we try and share the connection, the whole thing blocks
+ * indefinitely.
+ */
+
+#include "upstart-dbus.h"
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+
+#include <dbus/dbus.h>
+
+#include "dbus/Upstart.h"
+#include "dbus/Upstart_Job.h"
+#include "dbus/Upstart_Instance.h"
+
+#include <stdio.h>
+
+#define SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket"
+#define UPSTART_BUS_ADDRESS "unix:abstract=/com/ubuntu/upstart"
+#define UPSTART_SERVICE_NAME "com.ubuntu.Upstart"
+#define UPSTART_MANAGER_PATH "/com/ubuntu/Upstart"
+#define UPSTART_IFACE "com.ubuntu.Upstart0_6"
+#define UPSTART_JOB_IFACE UPSTART_IFACE ".Job"
+#define UPSTART_INSTANCE_IFACE UPSTART_IFACE ".Instance"
+#define UPSTART_ERROR_ALREADY_STARTED UPSTART_IFACE ".Error.AlreadyStarted"
+#define UPSTART_ERROR_UNKNOWN_INSTANCE UPSTART_IFACE ".Error.UnknownInstance"
+
+static DBusGConnection *
+get_connection(void)
+{
+ GError *error = NULL;
+ DBusGConnection *conn;
+
+ conn = dbus_g_bus_get_private(DBUS_BUS_SYSTEM, NULL, &error);
+
+ if (error)
+ {
+ g_error_free(error);
+ error = NULL;
+
+ conn = dbus_g_connection_open("unix:abstract=/com/ubuntu/upstart",
+ &error);
+
+ if (error)
+ {
+ g_warning("Can't connect to either system or Upstart "
+ "DBus bus.");
+ g_error_free(error);
+
+ return NULL;
+ }
+ }
+
+ return conn;
+}
+
+static DBusGProxy *
+new_proxy(DBusGConnection *conn, const gchar *object_path,
+ const gchar *iface)
+{
+ return dbus_g_proxy_new_for_name(conn,
+ UPSTART_SERVICE_NAME,
+ object_path,
+ iface);
+}
+
+static GHashTable *
+get_object_properties(DBusGProxy *obj, const gchar *iface)
+{
+ GError *error = NULL;
+ DBusGProxy *proxy;
+ GHashTable *asv;
+ GHashTable *retval;
+ GHashTableIter iter;
+ gpointer k, v;
+
+ proxy = dbus_g_proxy_new_from_proxy(obj,
+ DBUS_INTERFACE_PROPERTIES, NULL);
+
+ dbus_g_proxy_call(proxy, "GetAll", &error, G_TYPE_STRING,
+ iface, G_TYPE_INVALID,
+ dbus_g_type_get_map("GHashTable",
+ G_TYPE_STRING,
+ G_TYPE_VALUE),
+ &asv, G_TYPE_INVALID);
+
+ if (error) {
+ g_warning("Error getting %s properties: %s", iface, error->message);
+ g_error_free(error);
+ g_object_unref(proxy);
+ return NULL;
+ }
+
+ retval = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+
+ g_hash_table_iter_init(&iter, asv);
+ while (g_hash_table_iter_next(&iter, &k, &v)) {
+ gchar *key = k;
+ GValue *val = v;
+
+ /* all known properties are strings */
+ if (G_VALUE_TYPE(val) == G_TYPE_STRING) {
+ g_hash_table_insert(retval, g_strdup(key),
+ g_value_dup_string(val));
+ }
+ }
+
+ g_hash_table_destroy(asv);
+
+ return retval;
+}
+
+gchar **
+upstart_get_all_jobs(void)
+{
+ DBusGConnection *conn;
+ DBusGProxy *manager;
+ GError *error = NULL;
+ GPtrArray *array;
+ gchar **retval = NULL;
+ gint i, j;
+
+ conn = get_connection();
+ if (!conn)
+ return NULL;
+
+ manager = new_proxy(conn, UPSTART_MANAGER_PATH, UPSTART_IFACE);
+
+ dbus_g_proxy_call(manager, "GetAllJobs", &error, G_TYPE_INVALID,
+ dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
+ &array, G_TYPE_INVALID);
+
+ if (error)
+ {
+ g_warning("Can't call GetAllJobs: %s", error->message);
+ g_error_free(error);
+ g_object_unref(manager);
+ dbus_g_connection_unref(conn);
+ return NULL;
+ }
+
+ retval = g_new0(gchar *, array->len + 1);
+
+ for (i = 0, j = 0; i < array->len; i++)
+ {
+ DBusGProxy *job;
+
+ job = new_proxy(conn, g_ptr_array_index(array, i),
+ UPSTART_JOB_IFACE);
+
+ if (job) {
+ GHashTable *props = get_object_properties(job,
+ UPSTART_JOB_IFACE);
+
+ if (props) {
+ gchar *name = g_hash_table_lookup(props,
+ "name");
+
+ if (name)
+ retval[j++] = g_strdup(name);
+
+ g_hash_table_destroy(props);
+ }
+
+ g_object_unref(job);
+ }
+ }
+
+ g_ptr_array_free(array, TRUE);
+
+ g_object_unref(manager);
+ dbus_g_connection_unref(conn);
+
+ return retval;
+}
+
+static DBusGProxy *
+upstart_get_job_by_name(DBusGConnection *conn, DBusGProxy *manager,
+ const gchar *name)
+{
+ GError *error = NULL;
+ gchar *object_path;
+ DBusGProxy *retval;
+
+ dbus_g_proxy_call(manager, "GetJobByName", &error, G_TYPE_STRING,
+ name, G_TYPE_INVALID, DBUS_TYPE_G_OBJECT_PATH, &object_path,
+ G_TYPE_INVALID);
+
+ if (error)
+ {
+ g_warning("Error calling GetJobByName: %s", error->message);
+ g_error_free(error);
+ return NULL;
+ }
+
+ retval = new_proxy(conn, object_path, UPSTART_JOB_IFACE);
+
+ g_free(object_path);
+
+ return retval;
+}
+
+static gchar **
+get_job_instances(DBusGProxy *job)
+{
+ GError *error = NULL;
+ GPtrArray *array;
+ gchar **retval;
+ gint i;
+
+ dbus_g_proxy_call(job, "GetAllInstances", &error, G_TYPE_INVALID,
+ dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
+ &array, G_TYPE_INVALID);
+
+ if (error)
+ {
+ g_warning("Can't call GetAllInstances: %s", error->message);
+ g_error_free(error);
+ return NULL;
+ }
+
+ retval = g_new0(gchar *, array->len + 1);
+
+ for (i = 0; i < array->len; i++)
+ {
+ retval[i] = g_ptr_array_index(array, i);
+ }
+
+ g_ptr_array_free(array, TRUE);
+
+ return retval;
+}
+
+static DBusGProxy *
+get_first_instance(DBusGConnection *conn, DBusGProxy *job)
+{
+ gchar **instances;
+ DBusGProxy *instance = NULL;
+
+ instances = get_job_instances(job);
+
+ if (!instances)
+ return NULL;
+
+ if (*instances)
+ {
+ instance = new_proxy(conn, instances[0],
+ UPSTART_INSTANCE_IFACE);
+ }
+
+ g_strfreev(instances);
+ return instance;
+}
+
+gboolean
+upstart_job_is_running(const gchar *name)
+{
+ DBusGConnection *conn;
+ DBusGProxy *manager;
+ DBusGProxy *job;
+ gboolean retval = FALSE;
+
+ conn = get_connection();
+ if (!conn)
+ return FALSE;
+
+ manager = new_proxy(conn, UPSTART_MANAGER_PATH, UPSTART_IFACE);
+
+ job = upstart_get_job_by_name(conn, manager, name);
+ if (job) {
+ DBusGProxy *instance = get_first_instance(conn, job);
+
+ if (instance) {
+ GHashTable *props = get_object_properties(instance,
+ UPSTART_INSTANCE_IFACE);
+
+ if (props) {
+ const gchar *state = g_hash_table_lookup(props,
+ "state");
+
+ retval = !g_strcmp0(state, "running");
+
+ g_hash_table_destroy(props);
+ }
+
+ g_object_unref(instance);
+ }
+
+ g_object_unref(job);
+ }
+
+ g_object_unref(manager);
+ dbus_g_connection_unref(conn);
+
+ return retval;
+}
+
+gboolean
+upstart_job_do(const gchar *name, UpstartJobCommand cmd, const int timeout)
+{
+ DBusGConnection *conn;
+ DBusGProxy *manager;
+ DBusGProxy *job;
+ gboolean retval;
+
+ conn = get_connection();
+ if (!conn)
+ return FALSE;
+
+ manager = new_proxy(conn, UPSTART_MANAGER_PATH, UPSTART_IFACE);
+
+ job = upstart_get_job_by_name(conn, manager, name);
+ if (job) {
+ GError *error = NULL;
+ const gchar *cmd_name = NULL;
+ gchar *instance_path = NULL;
+ gchar *no_args[] = { NULL };
+
+ switch (cmd) {
+ case UPSTART_JOB_START:
+ cmd_name = "Start";
+ dbus_g_proxy_call_with_timeout (job, cmd_name,
+ timeout, &error,
+ G_TYPE_STRV, no_args,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_OBJECT_PATH, &instance_path,
+ G_TYPE_INVALID);
+ g_free (instance_path);
+ break;
+ case UPSTART_JOB_STOP:
+ cmd_name = "Stop";
+ dbus_g_proxy_call_with_timeout(job, cmd_name,
+ timeout, &error,
+ G_TYPE_STRV, no_args,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+ break;
+ case UPSTART_JOB_RESTART:
+ cmd_name = "Restart";
+ dbus_g_proxy_call_with_timeout (job, cmd_name,
+ timeout, &error,
+ G_TYPE_STRV, no_args,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_OBJECT_PATH, &instance_path,
+ G_TYPE_INVALID);
+ g_free (instance_path);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (error) {
+ g_warning("Could not issue %s: %s", cmd_name,
+ error->message);
+
+ /* ignore "already started" or "not running" errors */
+ if (dbus_g_error_has_name(error,
+ UPSTART_ERROR_ALREADY_STARTED) ||
+ dbus_g_error_has_name(error,
+ UPSTART_ERROR_UNKNOWN_INSTANCE)) {
+ retval = TRUE;
+ } else {
+ retval = FALSE;
+ }
+ g_error_free(error);
+ } else {
+ retval = TRUE;
+ }
+
+ g_object_unref(job);
+ } else {
+ retval = FALSE;
+ }
+
+ g_object_unref(manager);
+ dbus_g_connection_unref(conn);
+ return retval;
+}
+
+
diff --git a/lib/plugins/lrm/upstart-dbus.h b/lib/plugins/lrm/upstart-dbus.h
new file mode 100644
index 0000000..bc72c95
--- /dev/null
+++ b/lib/plugins/lrm/upstart-dbus.h
@@ -0,0 +1,36 @@
+/*
+ * 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.1 of the License, or (at your option) any later version.
+ *
+ * This software 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * File: upstart-dbus.c
+ * Copyright (C) 2010 Senko Rasic <senko.rasic@dobarkod.hr>
+ * Copyright (c) 2010 Ante Karamatic <ivoks@init.hr>
+ */
+#ifndef _UPSTART_DBUS_H_
+#define _UPSTART_DBUS_H_
+
+#include <glib.h>
+
+typedef enum {
+ UPSTART_JOB_START,
+ UPSTART_JOB_STOP,
+ UPSTART_JOB_RESTART
+} UpstartJobCommand;
+
+G_GNUC_INTERNAL gchar **upstart_get_all_jobs(void);
+G_GNUC_INTERNAL gboolean upstart_job_do(const gchar *name, UpstartJobCommand cmd, const int timeout);
+G_GNUC_INTERNAL gboolean upstart_job_is_running (const gchar *name);
+
+#endif /* _UPSTART_DBUS_H_ */
+