summaryrefslogtreecommitdiffstats
path: root/lib/plugins/lrm/raexecocf.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/plugins/lrm/raexecocf.c')
-rw-r--r--lib/plugins/lrm/raexecocf.c496
1 files changed, 496 insertions, 0 deletions
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));
+ }
+}
+