You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
heartbeat/lib/plugins/lrm/raexecocf.c

499 lines
13 KiB

/*
* 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 */
#define HADEBUGVAL "HA_DEBUG"
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)
{
uniform_ret_execra_t exit_value;
char ra_pathname[RA_MAX_NAME_LENGTH];
GHashTable * tmp_for_setenv;
GString * params_gstring;
char * inherit_debuglevel = NULL;
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);
}
/* execute the RA */
execl(ra_pathname, ra_pathname, op_type, (const char *)NULL);
cl_perror("(%s:%s:%d) execl 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 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, 0, 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;
}
stat(namelist[file_num]->d_name, &prop);
if (S_ISDIR(prop.st_mode)) {
free(namelist[file_num]);
continue;
}
snprintf(subdir,FILENAME_MAX,"%s/%s",
RA_PATH, namelist[file_num]->d_name);
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) {
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 (0 == g_str_tmp->len) {
pclose(file);
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);
pclose(file);
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 = 50;
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));
}
}