/*
* 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.
*/
#include <portability.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.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 lsb
#define PIL_PLUGINTYPE_S "RAExec"
#define PIL_PLUGIN_S "lsb"
#define PIL_PLUGINLICENSE LICENSE_PUBDOM
#define PIL_PLUGINLICENSEURL URL_PUBDOM
/* The begin of exported function list */
static int execra(const char * ra_name,
const char * op,
GHashTable * cmd_params,
GHashTable * env_params,
gboolean need_metadata,
int * call_key);
static int post_query_result(int exec_key, int * result, char ** meta_data);
/* 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 GHashTable * post_query_ops = NULL;
static int prepare_cmd_parameters(const char * raname, const char * op,
GHashTable * params, RA_ARGV params_argv);
static void params_hash_to_argv(gpointer key, gpointer value,
gpointer user_data);
static int raexec_setenv(GHashTable * env_params);
static int fork_and_execra(const char * ra_name, const char * op,
RA_ARGV ra_argv, GHashTable * env_params,
gboolean need_metadata);
static int read_pipe(int fd, char ** meta_data);
static int * g_intdup(gint value);
static void set_env(gpointer key, gpointer value, gpointer user_data);
/* The end of internal function & data list */
/* Rource agent execution plugin operations */
static struct RAExecOps raops =
{ execra,
post_query_result
};
/*
* The following two functions are only exported to the plugin infrastructure.
*/
/*
* raexec_closepi is called as part of shutting down the plugin.
* If there was any global data allocated, or file descriptors opened, etc.
* which is associated with the plugin, and not a single interface
* in particular, here's our chance to clean it up.
*/
static void raexec_closepi(PILPlugin *pi)
{
if ( post_query_ops != NULL ) {
g_hash_table_destroy(post_query_ops);
}
}
/*
* raexec_close_intf called as part of shutting down the md5 HBauth interface.
* If there was any global data allocated, or file descriptors opened, etc.
* which is associated with the md5 implementation, here's our chance
* to clean it up.
*/
static PIL_rc raexec_closeintf(PILInterface *pi, void *pd)
{
return PIL_OK;
}
PIL_PLUGIN_BOILERPLATE("1.0", Debug, raexec_closepi);
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);
post_query_ops = g_hash_table_new(g_int_hash, g_int_equal);
/* Register our interfaces */
return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S,
&raops, raexec_closeintf, &OurInterface, &OurImports,
interfprivate);
}
/*
* Real work starts here ;-)
*/
static int
execra( const char * ra_name, const char * op, GHashTable * cmd_params,
GHashTable * env_params, gboolean need_metadata, int * call_key)
{
RA_ARGV params_argv;
cl_log(LOG_DEBUG, "To execute a RA %s.", ra_name);
/* Prepare the call parameter */
if (0 > prepare_cmd_parameters(ra_name, op, cmd_params, params_argv)) {
cl_log(LOG_ERR, "lsb RA: Error of preparing parameters");
return -1;
}
/* Fork a child and execute the RA */
*call_key = fork_and_execra(ra_name, op, params_argv, env_params,
need_metadata);
if (*call_key <= 0) {
return -1;
}
else {
return 0; /* return ok */
}
}
static int
post_query_result(int exec_key, int * result, char ** meta_data)
{
int ret;
gpointer org_key, org_value;
gboolean found = FALSE;
if ( post_query_ops == NULL ) {
return -1;
}
found = g_hash_table_lookup_extended(post_query_ops,
&exec_key, &org_key, &org_value);
if ( !found ) {
cl_log(LOG_ERR,"No this child %d need post query.", exec_key);
return -1;
}
ret = waitpid(exec_key, result, WNOHANG);
if ( ret == 0 ) {
cl_log(LOG_DEBUG, "process %d don't exit yet.", exec_key);
/* return at once to avoid remove item in 'post_query_ops' */
return 0;
}
if ( ret == -1 ) {
cl_log(LOG_ERR, "error when fetching %d exit status.",
exec_key);
}
if ((ret > 0) && (*(int*)org_value > 0) && ( meta_data != NULL )) {
read_pipe(*(int*)org_value, meta_data);
}
g_free(org_key);
g_free(org_value);
g_hash_table_remove(post_query_ops, &exec_key);
return ret;
}
static int
prepare_cmd_parameters(const char * raname, const char * op,
GHashTable * params_ht, RA_ARGV params_argv)
{
/* For lsb init scripts, no corresponding definite specification
* But for lsb none-init scripts, maybe need it.
*/
int ht_size = 0;
int tmp_len;
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;
}
tmp_len = strnlen(raname, 160) + 1;
params_argv[0] = g_new(gchar, tmp_len);
strncpy(params_argv[0], raname, tmp_len);
tmp_len = strnlen(op, 160) + 1;
params_argv[1] = g_new(gchar, tmp_len);
strncpy(params_argv[1], op, tmp_len);
params_argv[ht_size+2] = NULL;
if (params_ht) {
g_hash_table_foreach(params_ht,
params_hash_to_argv, params_argv);
}
return 0;
}
static void
params_hash_to_argv(gpointer key, gpointer value, gpointer user_data)
{
int param_index;
RA_ARGV * ra_argv = user_data;
if (*ra_argv == NULL ) {
return;
}
/* the parameter index start from 1 */
/* and start from 2 in argv array */
param_index = atoi((char *)key);
(*ra_argv)[param_index + 1] = g_new(gchar, 21);
*((*ra_argv)[param_index + 1] + 20) = '\0';
strncpy((*ra_argv)[param_index +1], (char*)value,
strnlen((char*)value, 20));
}
static int
raexec_setenv(GHashTable * env_params)
{
/* For lsb init scripts, no corresponding definite specification
* But for lsb none-init scripts, maybe need it.
*/
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)
{
setenv((const char *)key, (const char *)value, 1);
/*Need to free the memory to which key and value point?*/
}
/* Possible bug for pipe using ?? */
static int
fork_and_execra(const char * ra_name, const char * op, RA_ARGV ra_argv,
GHashTable * env_params, gboolean need_metadata)
{
int cpid;
int fd[2];
cl_log(LOG_DEBUG, "Will to execute RA %s.", ra_name);
if (need_metadata == TRUE) {
if ( pipe(fd) < 0 ) {
cl_log(LOG_ERR,"pipe create error when to execute %s.",
ra_name);
return -1;
}
}
if ( (cpid=fork()) < 0 ) {
cl_log(LOG_ERR, "Fork failed when to execute %s.", ra_name);
return -1;
}
if ( cpid > 0 ) {
/* In parent process */
/* close write fd */
if ( need_metadata == TRUE ) {
close(fd[1]);
g_hash_table_insert(post_query_ops,
g_intdup(cpid), g_intdup(fd[0]));
} else {
g_hash_table_insert(post_query_ops,
g_intdup(cpid), g_intdup(0));
}
return cpid;
} else {
/* in child process */
/* close read fd */
cl_log(LOG_DEBUG, "In forked child %d.", getpid());
if ( need_metadata == TRUE ) {
close(fd[0]);
if ( fd[1] != STDOUT_FILENO ) {
if (dup2(fd[1], STDOUT_FILENO)!=STDOUT_FILENO) {
cl_log(LOG_ERR,"dup2 error when to "\
"execute RA.");
exit(-1);
}
}
close(fd[1]);
}
/* For lsb init scripts, no corresponding definite specification
* Not set calling parameterscl. But for lsb none-init scripts,
* should need
*/
raexec_setenv(env_params);
if ( execv(ra_name, ra_argv) < 0 ) {
cl_log(LOG_ERR, "execl error when to execute RA %s.",
ra_name);
}
exit(-1);
}
}
static int
read_pipe(int fd, char ** meta_data)
{
const int BUFFLEN = 81;
char buffer[BUFFLEN];
int readlen;
GString * gstr_tmp;
*meta_data = NULL;
gstr_tmp = g_string_new("");
do {
memset(buffer, 0, BUFFLEN);
readlen = read(fd, buffer, BUFFLEN - 1);
if ( readlen > 0 ) {
g_string_append(gstr_tmp, buffer);
}
} while (readlen == BUFFLEN - 1);
close(fd);
if (readlen < 0) {
cl_log(LOG_ERR, "read pipe error when execute RA.");
return -1;
}
if ( gstr_tmp->len == 0 ) {
cl_log(LOG_INFO, "read 0 byte from this pipe when execute RA.");
return 0;
}
*meta_data = malloc(gstr_tmp->len + 1);
if ( *meta_data == NULL ) {
cl_log(LOG_ERR, "malloc error in read_pipe.");
return -1;
}
(*meta_data)[0] = '\0';
(*meta_data)[gstr_tmp->len] = '\0';
strncpy(*meta_data, gstr_tmp->str, gstr_tmp->len);
g_string_free(gstr_tmp, TRUE);
return 0;
}
static int *
g_intdup(gint value)
{
gint * tmp;
tmp = g_new(gint, 1);
*tmp = value;
return tmp;
}
syntax highlighted by Code2HTML, v. 0.9.1