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/stonith/stonith.c

584 lines
13 KiB

/*
* Stonith API infrastructure.
*
* Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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 library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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
*
*/
#include <lha_internal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <dlfcn.h>
#include <dirent.h>
#include <glib.h>
#define ENABLE_PIL_DEFS_PRIVATE
#include <pils/plugin.h>
#include <pils/generic.h>
#include <stonith/stonith.h>
#include <stonith/stonith_plugin.h>
#define MALLOC StonithPIsys->imports->alloc
#define MALLOCT(t) (t*)(MALLOC(sizeof(t)))
#define REALLOC StonithPIsys->imports->mrealloc
#define STRDUP StonithPIsys->imports->mstrdup
#define FREE(p) {StonithPIsys->imports->mfree(p); (p) = NULL;}
#define LOG(args...) PILCallLog(StonithPIsys->imports->log, args)
#define EXTPINAME_S "external"
PILPluginUniv* StonithPIsys = NULL;
static GHashTable* Splugins = NULL;
static int init_pluginsys(void);
extern StonithImports stonithimports;
static PILGenericIfMgmtRqst Reqs[] =
{
{STONITH_TYPE_S, &Splugins, &stonithimports, NULL, NULL},
{NULL, NULL, NULL, NULL, NULL}
};
void PILpisysSetDebugLevel(int);
/* Initialize the plugin system... */
static int
init_pluginsys(void) {
if (StonithPIsys) {
return TRUE;
}
/* PILpisysSetDebugLevel(10); */
StonithPIsys = NewPILPluginUniv(STONITH_MODULES);
if (StonithPIsys) {
if (PILLoadPlugin(StonithPIsys, PI_IFMANAGER, "generic", Reqs)
!= PIL_OK){
fprintf(stderr, "generic plugin load failed\n");
DelPILPluginUniv(StonithPIsys);
StonithPIsys = NULL;
}
/*PILSetDebugLevel(StonithPIsys, PI_IFMANAGER, "generic", 10);*/
}else{
fprintf(stderr, "pi univ creation failed\n");
}
return StonithPIsys != NULL;
}
/*
* Create a new Stonith object of the requested type.
*/
Stonith *
stonith_new(const char * type)
{
StonithPlugin * sp = NULL;
struct stonith_ops* ops = NULL;
char * key;
char * subplugin;
char * typecopy;
if (!init_pluginsys()) {
return NULL;
}
if ((typecopy = STRDUP(type)) == NULL) {
return NULL;
}
if (((subplugin = strchr(typecopy, '/')) != NULL) &&
(strncmp(EXTPINAME_S, typecopy, strlen(EXTPINAME_S)) == 0)) {
*subplugin++ = 0; /* make two strings */
}
/* Look and see if it's already loaded... */
if (g_hash_table_lookup_extended(Splugins, typecopy
, (gpointer)&key, (gpointer)&ops)) {
/* Yes! Increment reference count */
PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S, typecopy, 1);
}else{ /* No. Try and load it... */
if (PILLoadPlugin(StonithPIsys, STONITH_TYPE_S, typecopy, NULL)
!= PIL_OK) {
FREE(typecopy);
return NULL;
}
/* Look up the plugin in the Splugins table */
if (!g_hash_table_lookup_extended(Splugins, typecopy
, (void*)&key, (void*)&ops)) {
/* OOPS! didn't find it(!?!)... */
PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S
, typecopy, -1);
FREE(typecopy);
return NULL;
}
}
if (ops != NULL) {
sp = ops->new((const char *)(subplugin));
if (sp != NULL) {
sp->s.stype = STRDUP(typecopy);
}
}
FREE(typecopy);
return sp ? (&sp->s) : NULL;
}
static int
qsort_string_cmp(const void *a, const void *b)
{
return(strcmp(*(const char * const *)a, *(const char * const *)b));
}
/*
* Return list of STONITH types valid in stonith_new()
*/
char **
stonith_types(void)
{
int plugincount;
static char ** lasttypelist = NULL;
char ** newtypelist;
int extplugin = -1;
if (!init_pluginsys()) {
return NULL;
}
if (lasttypelist) {
stonith_free_hostlist(lasttypelist);
lasttypelist = NULL;
}
newtypelist = PILListPlugins(StonithPIsys, STONITH_TYPE_S, NULL);
if (newtypelist == NULL) {
return NULL;
}
/* look for 'external' plugin */
for (plugincount=0; newtypelist[plugincount] != NULL; ++plugincount) {
if (strcmp(newtypelist[plugincount], EXTPINAME_S) == 0) {
extplugin = plugincount;
}
}
if (extplugin >= 0) {
/* 'external' plugin exists, adjust types list by replacing
* 'external' with sorted list of 'external/subplugin' */
const char **extPI, **p;
int numextPI, i, index;
Stonith * ext;
/* let the external plugin return a list */
if ((ext = stonith_new(EXTPINAME_S)) == NULL) {
LOG(PIL_CRIT, "Cannot create new external "
"plugin object");
goto types_exit;
}
if ((extPI = stonith_get_confignames(ext)) == NULL) {
LOG(PIL_CRIT, "Cannot get external plugin subplugins");
stonith_delete(ext);
goto types_exit;
}
/* count the external plugins */
for (numextPI = 0, p = extPI; *p; p++, numextPI++);
/* sort the external plugins */
qsort(extPI, numextPI, sizeof(char *), qsort_string_cmp);
/* allocate types list (don't need numextPI+plugincount+1
* because 'external' is being removed from list */
lasttypelist = (char **)
MALLOC((numextPI+plugincount) * sizeof(char *));
if (lasttypelist == NULL) {
LOG(PIL_CRIT, "Out of memory");
stonith_delete(ext);
goto types_exit;
}
memset(lasttypelist, 0, (numextPI+plugincount)*sizeof(char *));
/* copy plugins up to but not including 'external' */
for (index = 0, i = 0; i < extplugin; index++, i++) {
lasttypelist[index] = STRDUP(newtypelist[i]);
if (lasttypelist[index] == NULL) {
LOG(PIL_CRIT, "Out of memory");
stonith_delete(ext);
goto types_exit_mem;
}
}
/* copy external plugins */
for (i = 0; i < numextPI; index++, i++) {
int len = strlen(EXTPINAME_S) +
strlen(extPI[i]) + 2;
lasttypelist[index] = MALLOC(len * sizeof(char *));
if (lasttypelist[index] == NULL) {
LOG(PIL_CRIT, "Out of memory");
stonith_delete(ext);
goto types_exit_mem;
}
snprintf(lasttypelist[index], len, "%s/%s"
, EXTPINAME_S, extPI[i]);
}
/* copy plugins after 'external' */
for (i = extplugin+1; i < plugincount; index++, i++) {
lasttypelist[index] = STRDUP(newtypelist[i]);
if (lasttypelist[index] == NULL) {
LOG(PIL_CRIT, "Out of memory");
stonith_delete(ext);
goto types_exit_mem;
}
}
stonith_delete(ext);
}else{
/* 'external' plugin doesn't exist, copy types list */
char **from, **to;
lasttypelist = (char**)MALLOC((plugincount+1) * sizeof(char *));
if (lasttypelist == NULL) {
LOG(PIL_CRIT, "Out of memory");
goto types_exit;
}
for (from = newtypelist, to = lasttypelist
; *from
; ++from, ++to) {
*to = STRDUP(*from);
if (*to == NULL) {
LOG(PIL_CRIT, "Out of memory");
goto types_exit_mem;
}
}
*to = NULL;
}
goto types_exit;
types_exit_mem:
stonith_free_hostlist(lasttypelist);
lasttypelist = NULL;
types_exit:
PILFreePluginList(newtypelist);
return lasttypelist;
}
/* Destroy the STONITH object... */
void
stonith_delete(Stonith *s)
{
StonithPlugin* sp = (StonithPlugin*)s;
if (sp && sp->s_ops) {
char * st = sp->s.stype;
sp->s_ops->destroy(sp);
PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S, st, -1);
/* destroy should not free it */
FREE(st);
}
}
const char **
stonith_get_confignames(Stonith* s)
{
StonithPlugin* sp = (StonithPlugin*)s;
if (sp && sp->s_ops) {
return sp->s_ops->get_confignames(sp);
}
return NULL;
}
const char*
stonith_get_info(Stonith* s, int infotype)
{
StonithPlugin* sp = (StonithPlugin*)s;
if (sp && sp->s_ops) {
return sp->s_ops->get_info(sp, infotype);
}
return NULL;
}
void
stonith_set_debug (Stonith* s, int debuglevel)
{
StonithPlugin* sp = (StonithPlugin*)s;
if (StonithPIsys == NULL) {
return;
}
PILSetDebugLevel(StonithPIsys, STONITH_TYPE_S, sp->s.stype, debuglevel);
}
void
stonith_set_log(Stonith* s, PILLogFun logfun)
{
if (StonithPIsys == NULL) {
return;
}
PilPluginUnivSetLog(StonithPIsys, logfun);
}
int
stonith_set_config(Stonith* s, StonithNVpair* list)
{
StonithPlugin* sp = (StonithPlugin*)s;
if (sp && sp->s_ops) {
int rc = sp->s_ops->set_config(sp, list);
if (rc == S_OK) {
sp->isconfigured = TRUE;
}
return rc;
}
return S_INVAL;
}
/*
* FIXME: We really ought to support files with name=value type syntax
* on each line...
*
*/
int
stonith_set_config_file(Stonith* s, const char * configname)
{
FILE * cfgfile;
char line[1024];
if ((cfgfile = fopen(configname, "r")) == NULL) {
LOG(PIL_CRIT, "Cannot open %s", configname);
return(S_BADCONFIG);
}
while (fgets(line, sizeof(line), cfgfile) != NULL){
int len;
if (*line == '#' || *line == '\n' || *line == EOS) {
continue;
}
/*remove the new line in the end*/
len = strnlen(line, sizeof(line)-1);
if (line[len-1] == '\n'){
line[len-1] = '\0';
}else{
line[len] = '\0';
}
fclose(cfgfile);
return stonith_set_config_info(s, line);
}
fclose(cfgfile);
return S_BADCONFIG;
}
int
stonith_set_config_info(Stonith* s, const char * info)
{
StonithNVpair* cinfo;
int rc;
cinfo = stonith1_compat_string_to_NVpair(s, info);
if (cinfo == NULL) {
return S_BADCONFIG;
}
rc = stonith_set_config(s, cinfo);
free_NVpair(cinfo); cinfo = NULL;
return rc;
}
char**
stonith_get_hostlist(Stonith* s)
{
StonithPlugin* sp = (StonithPlugin*)s;
if (sp && sp->s_ops && sp->isconfigured) {
return sp->s_ops->get_hostlist(sp);
}
return NULL;
}
void
stonith_free_hostlist(char** hostlist)
{
char ** here;
for (here=hostlist; *here; ++here) {
FREE(*here);
}
FREE(hostlist);
}
int
stonith_get_status(Stonith* s)
{
StonithPlugin* sp = (StonithPlugin*)s;
if (sp && sp->s_ops && sp->isconfigured) {
return sp->s_ops->get_status(sp);
}
return S_INVAL;
}
int
stonith_req_reset(Stonith* s, int operation, const char* node)
{
StonithPlugin* sp = (StonithPlugin*)s;
if (sp && sp->s_ops && sp->isconfigured) {
char* nodecopy = STRDUP(node);
int rc;
if (nodecopy == NULL) {
return S_OOPS;
}
g_strdown(nodecopy);
rc = sp->s_ops->req_reset(sp, operation, nodecopy);
FREE(nodecopy);
return rc;
}
return S_INVAL;
}
/* Stonith 1 compatibility: Convert a string to an NVpair set */
StonithNVpair*
stonith1_compat_string_to_NVpair(Stonith* s, const char * str)
{
/* We make some assumptions that the order of parameters in the
* result from stonith_get_confignames() matches that which
* was required from a Stonith1 module.
* Everything after the last delimiter is passed along as part of
* the final argument - white space and all...
*/
const char ** config_names;
int n_names;
int j;
const char * delims = " \t\n\r\f";
StonithNVpair* ret;
if ((config_names = stonith_get_confignames(s)) == NULL) {
return NULL;
}
for (n_names=0; config_names[n_names] != NULL; ++n_names) {
/* Just count */;
}
ret = (StonithNVpair*) (MALLOC((n_names+1)*sizeof(StonithNVpair)));
if (ret == NULL) {
return NULL;
}
memset(ret, 0, (n_names+1)*sizeof(StonithNVpair));
for (j=0; j < n_names; ++j) {
size_t len;
if ((ret[j].s_name = STRDUP(config_names[j])) == NULL) {
goto freeandexit;
}
ret[j].s_value = NULL;
str += strspn(str, delims);
if (*str == EOS) {
goto freeandexit;
}
if (j == (n_names -1)) {
len = strlen(str);
}else{
len = strcspn(str, delims);
}
if ((ret[j].s_value = MALLOC((len+1)*sizeof(char))) == NULL) {
goto freeandexit;
}
memcpy(ret[j].s_value, str, len);
ret[j].s_value[len] = EOS;
str += len;
}
ret[j].s_name = NULL;
return ret;
freeandexit:
free_NVpair(ret); ret = NULL;
return NULL;
}
static int NVcur = -1;
static int NVmax = -1;
static gboolean NVerr = FALSE;
static void
stonith_walk_ghash(gpointer key, gpointer value, gpointer user_data)
{
StonithNVpair* u = user_data;
if (NVcur <= NVmax && !NVerr) {
u[NVcur].s_name = STRDUP(key);
u[NVcur].s_value = STRDUP(value);
if (u[NVcur].s_name == NULL || u[NVcur].s_value == NULL) {
/* Memory allocation error */
NVerr = TRUE;
return;
}
++NVcur;
}else{
NVerr = TRUE;
}
}
StonithNVpair*
stonith_ghash_to_NVpair(GHashTable* stringtable)
{
int hsize = g_hash_table_size(stringtable);
StonithNVpair* ret;
if ((ret = (StonithNVpair*)MALLOC(sizeof(StonithNVpair)*(hsize+1))) == NULL) {
return NULL;
}
NVmax = hsize;
NVcur = 0;
ret[hsize].s_name = NULL;
ret[hsize].s_value = NULL;
g_hash_table_foreach(stringtable, stonith_walk_ghash, ret);
NVmax = NVcur = -1;
if (NVerr) {
free_NVpair(ret);
ret = NULL;
}
return ret;
}
void
free_NVpair(StonithNVpair* nv)
{
StonithNVpair* this;
if (nv == NULL) {
return;
}
for (this=nv; this->s_name; ++this) {
FREE(this->s_name);
if (this->s_value) {
FREE(this->s_value);
}
}
FREE(nv);
}