1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public
4  * License as published by the Free Software Foundation; either
5  * version 2.1 of the License, or (at your option) any later version.
6  *
7  * This software is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  * General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public
13  * License along with this library; if not, write to the Free Software
14  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15  *
16  * File: raexechb.c
17  * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
18  * Copyright (c) 2004 International Business Machines
19  *
20  * This code implements the Resource Agent Plugin Module for LSB style.
21  * It's a part of Local Resource Manager. Currently it's used by lrmd only.
22  */
23 
24 #include <lha_internal.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #include <sys/stat.h>
32 #include <errno.h>
33 #include <dirent.h>
34 #include <libgen.h>
35 #include <glib.h>
36 #include <clplumbing/cl_log.h>
37 #include <pils/plugin.h>
38 #include <lrm/raexec.h>
39 
40 #define PIL_PLUGINTYPE		RA_EXEC_TYPE
41 #define PIL_PLUGIN		heartbeat
42 #define PIL_PLUGINTYPE_S	"RAExec"
43 #define PIL_PLUGIN_S		"heartbeat"
44 #define PIL_PLUGINLICENSE	LICENSE_PUBDOM
45 #define PIL_PLUGINLICENSEURL	URL_PUBDOM
46 
47 static const char * RA_PATH = HB_RA_DIR;
48 
49 static const char meta_data_template[] =
50 "<?xml version=\"1.0\"?>\n"
51 "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
52 "<resource-agent name=\"%s\">\n"
53 "<version>1.0</version>\n"
54 "<longdesc lang=\"en\">\n"
55 "%s"
56 "</longdesc>\n"
57 "<shortdesc lang=\"en\">%s</shortdesc>\n"
58 "<parameters>\n"
59 "<parameter name=\"1\" unique=\"1\" required=\"0\">\n"
60 "<longdesc lang=\"en\">\n"
61 "This argument will be passed as the first argument to the "
62 "heartbeat resource agent (assuming it supports one)\n"
63 "</longdesc>\n"
64 "<shortdesc lang=\"en\">argv[1]</shortdesc>\n"
65 "<content type=\"string\" default=\" \" />\n"
66 "</parameter>\n"
67 "<parameter name=\"2\" unique=\"1\" required=\"0\">\n"
68 "<longdesc lang=\"en\">\n"
69 "This argument will be passed as the second argument to the "
70 "heartbeat resource agent (assuming it supports one)\n"
71 "</longdesc>\n"
72 "<shortdesc lang=\"en\">argv[2]</shortdesc>\n"
73 "<content type=\"string\" default=\" \" />\n"
74 "</parameter>\n"
75 "<parameter name=\"3\" unique=\"1\" required=\"0\">\n"
76 "<longdesc lang=\"en\">\n"
77 "This argument will be passed as the third argument to the "
78 "heartbeat resource agent (assuming it supports one)\n"
79 "</longdesc>\n"
80 "<shortdesc lang=\"en\">argv[3]</shortdesc>\n"
81 "<content type=\"string\" default=\" \" />\n"
82 "</parameter>\n"
83 "<parameter name=\"4\" unique=\"1\" required=\"0\">\n"
84 "<longdesc lang=\"en\">\n"
85 "This argument will be passed as the fourth argument to the "
86 "heartbeat resource agent (assuming it supports one)\n"
87 "</longdesc>\n"
88 "<shortdesc lang=\"en\">argv[4]</shortdesc>\n"
89 "<content type=\"string\" default=\" \" />\n"
90 "</parameter>\n"
91 "<parameter name=\"5\" unique=\"1\" required=\"0\">\n"
92 "<longdesc lang=\"en\">\n"
93 "This argument will be passed as the fifth argument to the "
94 "heartbeat resource agent (assuming it supports one)\n"
95 "</longdesc>\n"
96 "<shortdesc lang=\"en\">argv[5]</shortdesc>\n"
97 "<content type=\"string\" default=\" \" />\n"
98 "</parameter>\n"
99 "</parameters>\n"
100 "<actions>\n"
101 "<action name=\"start\"   timeout=\"15\" />\n"
102 "<action name=\"stop\"    timeout=\"15\" />\n"
103 "<action name=\"status\"  timeout=\"15\" />\n"
104 "<action name=\"monitor\" timeout=\"15\" interval=\"15\" start-delay=\"15\" />\n"
105 "<action name=\"meta-data\"  timeout=\"5\" />\n"
106 "</actions>\n"
107 "<special tag=\"heartbeart\">\n"
108 "</special>\n"
109 "</resource-agent>\n";
110 
111 /* The begin of exported function list */
112 static int execra(const char * rsc_id,
113 		  const char * rsc_type,
114 		  const char * provider,
115 		  const char * op_type,
116 		  const int    timeout,
117 	 	  GHashTable * params);
118 
119 static uniform_ret_execra_t map_ra_retvalue(int ret_execra
120 	, const char * op_type, const char * std_output);
121 static int get_resource_list(GList ** rsc_info);
122 static char* get_resource_meta(const char* rsc_type,  const char* provider);
123 static int get_provider_list(const char* ra_type, GList ** providers);
124 
125 /* The end of exported function list */
126 
127 /* The begin of internal used function & data list */
128 #define MAX_PARAMETER_NUM 40
129 typedef char * RA_ARGV[MAX_PARAMETER_NUM];
130 
131 static const int MAX_LENGTH_OF_RSCNAME = 40,
132 		 MAX_LENGTH_OF_OPNAME = 40;
133 
134 static int prepare_cmd_parameters(const char * rsc_type, const char * op_type,
135 		GHashTable * params, RA_ARGV params_argv);
136 /* The end of internal function & data list */
137 
138 /* Rource agent execution plugin operations */
139 static struct RAExecOps raops =
140 {	execra,
141 	map_ra_retvalue,
142 	get_resource_list,
143 	get_provider_list,
144 	get_resource_meta
145 };
146 
147 PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
148 
149 static const PILPluginImports*  PluginImports;
150 static PILPlugin*               OurPlugin;
151 static PILInterface*		OurInterface;
152 static void*			OurImports;
153 static void*			interfprivate;
154 static int			idebuglevel = 0;
155 
156 /*
157  * Our plugin initialization and registration function
158  * It gets called when the plugin gets loaded.
159  */
160 PIL_rc
161 PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
162 
163 PIL_rc
PIL_PLUGIN_INIT(PILPlugin * us,const PILPluginImports * imports)164 PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
165 {
166 	/* Force the compiler to do a little type checking */
167 	(void)(PILPluginInitFun)PIL_PLUGIN_INIT;
168 
169 	PluginImports = imports;
170 	OurPlugin = us;
171 
172 	/* Register ourself as a plugin */
173 	imports->register_plugin(us, &OurPIExports);
174 
175 	if (getenv(HADEBUGVAL) != NULL && atoi(getenv(HADEBUGVAL)) > 0 ) {
176 		idebuglevel = atoi(getenv(HADEBUGVAL));
177 		cl_log(LOG_DEBUG, "LRM debug level set to %d", idebuglevel);
178 	}
179 
180 	/*  Register our interfaces */
181  	return imports->register_interface(us, PIL_PLUGINTYPE_S,  PIL_PLUGIN_S,
182 		&raops, NULL, &OurInterface, &OurImports,
183 		interfprivate);
184 }
185 
186 /*
187  *	Real work starts here ;-)
188  */
189 
190 static int
execra(const char * rsc_id,const char * rsc_type,const char * provider,const char * op_type,const int timeout,GHashTable * params)191 execra( const char * rsc_id, const char * rsc_type, const char * provider,
192 	const char * op_type, const int timeout, GHashTable * params)
193 {
194 	RA_ARGV params_argv;
195 	char ra_pathname[RA_MAX_NAME_LENGTH];
196 	uniform_ret_execra_t exit_value;
197 	GString * debug_info;
198 	char * optype_tmp = NULL;
199 	int index_tmp = 0;
200 
201 	/* How to generate the meta-data? There is nearly no value
202 	 * information in meta-data build up in current way.
203 	 * Should directly add meta-data to the script itself?
204 	 */
205 	if ( 0 == STRNCMP_CONST(op_type, "meta-data") ) {
206 		printf("%s", get_resource_meta(rsc_type, provider));
207 		exit(0);
208 	}
209 
210 	/* To simulate the 'monitor' operation with 'status'.
211 	 * Now suppose there is no 'monitor' operation for heartbeat scripts.
212 	 */
213 	if ( 0 == STRNCMP_CONST(op_type, "monitor") ) {
214 		optype_tmp = g_strdup("status");
215 	} else {
216 		optype_tmp = g_strdup(op_type);
217 	}
218 
219 	/* Prepare the call parameter */
220 	if (0 > prepare_cmd_parameters(rsc_type, optype_tmp, params, params_argv)) {
221 		cl_log(LOG_ERR, "HB RA: Error of preparing parameters");
222 		g_free(optype_tmp);
223 		return -1;
224 	}
225 	g_free(optype_tmp);
226 
227 	get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname);
228 
229 	/* let this log show only high loglevel. */
230 	if (idebuglevel  > 1) {
231 		debug_info = g_string_new("");
232 		do {
233 			g_string_append(debug_info, params_argv[index_tmp]);
234 			g_string_append(debug_info, " ");
235 		} while (params_argv[++index_tmp] != NULL);
236 		debug_info->str[debug_info->len-1] = '\0';
237 
238 		cl_log(LOG_DEBUG, "RA instance %s executing: heartbeat::%s"
239 			, rsc_id, debug_info->str);
240 
241 		g_string_free(debug_info, TRUE);
242 	}
243 
244 	closefiles(); /* don't leak open files */
245 	execv(ra_pathname, params_argv);
246 	cl_perror("(%s:%s:%d) execv failed for %s"
247 		  , __FILE__, __FUNCTION__, __LINE__, ra_pathname);
248 
249 	switch (errno) {
250 		case ENOENT:   /* No such file or directory */
251 		case EISDIR:   /* Is a directory */
252 			exit_value = EXECRA_NOT_INSTALLED;
253 			break;
254 		default:
255 			exit_value = EXECRA_EXEC_UNKNOWN_ERROR;
256         }
257         exit(exit_value);
258 }
259 
260 static int
prepare_cmd_parameters(const char * rsc_type,const char * op_type,GHashTable * params_ht,RA_ARGV params_argv)261 prepare_cmd_parameters(const char * rsc_type, const char * op_type,
262 	GHashTable * params_ht, RA_ARGV params_argv)
263 {
264 	int tmp_len, index;
265 	int ht_size = 0;
266 	int param_num = 0;
267 	char buf_tmp[20];
268 	void * value_tmp;
269 
270 	if (params_ht) {
271 		ht_size = g_hash_table_size(params_ht);
272 	}
273 	if ( ht_size+3 > MAX_PARAMETER_NUM ) {
274 		cl_log(LOG_ERR, "Too many parameters");
275 		return -1;
276 	}
277 
278 	/* Now suppose the parameter format stored in Hashtabe is as like as
279 	 * key="1", value="-Wl,soname=test"
280 	 * Moreover, the key is supposed as a string transfered from an integer.
281 	 * It may be changed in the future.
282 	 */
283 	/* Notice: if ht_size==0, no actual arguments except op_type */
284 	for (index = 1; index <= ht_size; index++ ) {
285 		snprintf(buf_tmp, sizeof(buf_tmp), "%d", index);
286 		value_tmp = g_hash_table_lookup(params_ht, buf_tmp);
287 		/* suppose the key is consecutive */
288 		if ( value_tmp == NULL ) {
289 /*			cl_log(LOG_WARNING, "Parameter ordering error in"\
290 				"prepare_cmd_parameters, raexeclsb.c");
291 			cl_log(LOG_WARNING, "search key=%s.", buf_tmp);
292 */			continue;
293                 }
294 		param_num ++;
295 		params_argv[param_num] = g_strdup((char *)value_tmp);
296 	}
297 
298 	tmp_len = strnlen(rsc_type, MAX_LENGTH_OF_RSCNAME);
299 	params_argv[0] = g_strndup(rsc_type, tmp_len);
300 	/* Add operation code as the last argument */
301 	tmp_len = strnlen(op_type, MAX_LENGTH_OF_OPNAME);
302 	params_argv[param_num+1] = g_strndup(op_type, tmp_len);
303 	/* Add the teminating NULL pointer */
304 	params_argv[param_num+2] = NULL;
305 	return 0;
306 }
307 
308 static uniform_ret_execra_t
map_ra_retvalue(int ret_execra,const char * op_type,const char * std_output)309 map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
310 {
311 
312 	/* Now there is no formal related specification for Heartbeat RA
313 	 * scripts. Temporarily deal as LSB init script.
314 	 */
315 	/* Except op_type equals 'status', the UNIFORM_RET_EXECRA is compatible
316 	   with LSB standard.
317 	*/
318 	const char * stop_pattern1 = "*stopped*",
319 		   * stop_pattern2 = "*not*running*",
320 		   * running_pattern1 = "*running*",
321 		   * running_pattern2 = "*OK*";
322 	char * lower_std_output = NULL;
323 
324 	if(ret_execra == EXECRA_NOT_INSTALLED) {
325 		return ret_execra;
326 	}
327 
328 	if (	0 == STRNCMP_CONST(op_type, "status")
329 	||	0 == STRNCMP_CONST(op_type, "monitor")) {
330 		if (std_output == NULL ) {
331 			cl_log(LOG_WARNING, "No status output from the (hb) resource agent.");
332 			return EXECRA_NOT_RUNNING;
333 		}
334 
335 		if (idebuglevel) {
336 			cl_log(LOG_DEBUG, "RA output was: [%s]", std_output);
337 		}
338 
339 	 	lower_std_output = g_ascii_strdown(std_output, -1);
340 
341 		if ( TRUE == g_pattern_match_simple(stop_pattern1
342 			, lower_std_output) || TRUE ==
343 			g_pattern_match_simple(stop_pattern2
344 			, lower_std_output) ) {
345 			if (idebuglevel) {
346 				cl_log(LOG_DEBUG
347 				,	"RA output [%s] matched stopped pattern"
348 				" [%s] or [%s]"
349 				,	std_output
350 				,	stop_pattern1
351 				,	stop_pattern2);
352 			}
353 			ret_execra = EXECRA_NOT_RUNNING; /* stopped */
354 		} else if ( TRUE == g_pattern_match_simple(running_pattern1
355 			, lower_std_output) || TRUE ==
356 			g_pattern_match_simple(running_pattern2
357 			, std_output) ) {
358 			if (idebuglevel) {
359 				cl_log(LOG_DEBUG
360 				,	"RA output [%s] matched running"
361 				" pattern [%s] or [%s]"
362 				,	std_output, running_pattern1
363 				,	running_pattern2);
364 			}
365 			ret_execra = EXECRA_OK; /* running */
366 		} else {
367 			/* It didn't say it was running - must be stopped */
368 			cl_log(LOG_DEBUG, "RA output [%s] didn't match any pattern"
369 			,	std_output);
370 			ret_execra = EXECRA_NOT_RUNNING; /* stopped */
371 		}
372 		g_free(lower_std_output);
373 	}
374 	/* For non-status operation return code */
375 	if (ret_execra < 0) {
376 		ret_execra = EXECRA_UNKNOWN_ERROR;
377 	}
378 	return ret_execra;
379 }
380 
381 static int
get_resource_list(GList ** rsc_info)382 get_resource_list(GList ** rsc_info)
383 {
384 	return get_runnable_list(RA_PATH, rsc_info);
385 }
386 
387 static char*
get_resource_meta(const char * rsc_type,const char * provider)388 get_resource_meta(const char* rsc_type,  const char* provider)
389 {
390 	GString * meta_data;
391 
392 	meta_data = g_string_new("");
393 	g_string_sprintf( meta_data, meta_data_template, rsc_type
394 			, rsc_type, rsc_type);
395 	return meta_data->str;
396 }
397 static int
get_provider_list(const char * ra_type,GList ** providers)398 get_provider_list(const char* ra_type, GList ** providers)
399 {
400         if ( providers == NULL ) {
401                 cl_log(LOG_ERR, "%s:%d: Parameter error: providers==NULL"
402                         , __FUNCTION__, __LINE__);
403                 return -2;
404         }
405 
406         if ( *providers != NULL ) {
407                 cl_log(LOG_ERR, "%s:%d: Parameter error: *providers==NULL."
408                         "This will cause memory leak."
409                         , __FUNCTION__, __LINE__);
410         }
411 
412         /* Now temporarily make it fixed */
413         *providers = g_list_append(*providers, g_strdup("heartbeat"));
414 
415         return g_list_length(*providers);
416 }
417