diff options
Diffstat (limited to '')
-rw-r--r-- | lib/plugins/lrm/Makefile.am | 58 | ||||
-rw-r--r-- | lib/plugins/lrm/dbus/Makefile.am | 16 | ||||
-rw-r--r-- | lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml | 45 | ||||
-rw-r--r-- | lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml | 71 | ||||
-rw-r--r-- | lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml | 57 | ||||
-rw-r--r-- | lib/plugins/lrm/raexechb.c | 416 | ||||
-rw-r--r-- | lib/plugins/lrm/raexeclsb.c | 609 | ||||
-rw-r--r-- | lib/plugins/lrm/raexecocf.c | 496 | ||||
-rw-r--r-- | lib/plugins/lrm/raexecupstart.c | 222 | ||||
-rw-r--r-- | lib/plugins/lrm/upstart-dbus.c | 406 | ||||
-rw-r--r-- | lib/plugins/lrm/upstart-dbus.h | 36 |
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_ */ + |