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