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: raexecocf.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 <libgen.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <sys/stat.h>
33 #include <errno.h>
34 #include <glib.h>
35 #include <clplumbing/cl_log.h>
36 #include <clplumbing/realtime.h>
37 #include <pils/plugin.h>
38 #include <dirent.h>
39 #include <libgen.h>  /* Add it for compiling on OSX */
40 #ifdef HAVE_TIME_H
41 #include <time.h>
42 #endif
43 
44 #include <lrm/raexec.h>
45 
46 # define PIL_PLUGINTYPE		RA_EXEC_TYPE
47 # define PIL_PLUGINTYPE_S	"RAExec"
48 # define PIL_PLUGINLICENSE	LICENSE_PUBDOM
49 # define PIL_PLUGINLICENSEURL	URL_PUBDOM
50 
51 # define PIL_PLUGIN		ocf
52 # define PIL_PLUGIN_S		"ocf"
53 /*
54  * Are there multiple paths? Now according to OCF spec, the answer is 'no'.
55  * But actually or for future?
56  */
57 static const char * RA_PATH = OCF_RA_DIR;
58 
59 /* The begin of exported function list */
60 static int execra(const char * rsc_id,
61 		  const char * rsc_type,
62 		  const char * provider,
63 		  const char * op_type,
64 		  const int    timeout,
65 	 	  GHashTable * params);
66 static uniform_ret_execra_t map_ra_retvalue(int ret_execra,
67 	   const char * op_type, const char * std_output);
68 static int get_resource_list(GList ** rsc_info);
69 static char* get_resource_meta(const char* rsc_type,  const char* provider);
70 static int get_provider_list(const char* ra_type, GList ** providers);
71 
72 /* The end of exported function list */
73 
74 /* The begin of internal used function & data list */
75 static void add_OCF_prefix(GHashTable * params, GHashTable * new_params);
76 static void add_OCF_env_vars(GHashTable * env, const char * rsc_id,
77 			     const char * rsc_type, const char * provider);
78 static void add_prefix_foreach(gpointer key, gpointer value,
79 				   gpointer user_data);
80 
81 static void hash_to_str(GHashTable * , GString *);
82 static void hash_to_str_foreach(gpointer key, gpointer value,
83 				   gpointer user_data);
84 
85 static int raexec_setenv(GHashTable * env_params);
86 static void set_env(gpointer key, gpointer value, gpointer user_data);
87 
88 static gboolean let_remove_eachitem(gpointer key, gpointer value,
89 				    gpointer user_data);
90 static int get_providers(const char* class_path, const char* op_type,
91 			 GList ** providers);
92 static void merge_string_list(GList** old, GList* new);
93 static gint compare_str(gconstpointer a, gconstpointer b);
94 
95 /* The end of internal function & data list */
96 
97 /* Rource agent execution plugin operations */
98 static struct RAExecOps raops =
99 {	execra,
100 	map_ra_retvalue,
101 	get_resource_list,
102 	get_provider_list,
103 	get_resource_meta
104 };
105 
106 PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
107 
108 static const PILPluginImports*  PluginImports;
109 static PILPlugin*               OurPlugin;
110 static PILInterface*		OurInterface;
111 static void*			OurImports;
112 static void*			interfprivate;
113 
114 /*
115  * Our plugin initialization and registration function
116  * It gets called when the plugin gets loaded.
117  */
118 PIL_rc
119 PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
120 
121 PIL_rc
PIL_PLUGIN_INIT(PILPlugin * us,const PILPluginImports * imports)122 PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
123 {
124 	/* Force the compiler to do a little type checking */
125 	(void)(PILPluginInitFun)PIL_PLUGIN_INIT;
126 
127 	PluginImports = imports;
128 	OurPlugin = us;
129 
130 	/* Register ourself as a plugin */
131 	imports->register_plugin(us, &OurPIExports);
132 
133 	/*  Register our interfaces */
134  	return imports->register_interface(us, PIL_PLUGINTYPE_S,  PIL_PLUGIN_S,
135 		&raops, NULL, &OurInterface, &OurImports,
136 		interfprivate);
137 }
138 
139 /*
140  * The function to execute a RA.
141  */
142 static int
execra(const char * rsc_id,const char * rsc_type,const char * provider,const char * op_type,const int timeout,GHashTable * params)143 execra(const char * rsc_id, const char * rsc_type, const char * provider,
144        const char * op_type, const int timeout, GHashTable * params)
145 {
146 	char ra_pathname[RA_MAX_NAME_LENGTH];
147 	GHashTable * tmp_for_setenv;
148 	GString * params_gstring;
149 	char * inherit_debuglevel = NULL;
150 	int save_errno;
151 
152 	get_ra_pathname(RA_PATH, rsc_type, provider, ra_pathname);
153 
154 	/* Setup environment correctly */
155 	tmp_for_setenv = g_hash_table_new(g_str_hash, g_str_equal);
156 	add_OCF_prefix(params, tmp_for_setenv);
157 	add_OCF_env_vars(tmp_for_setenv, rsc_id, rsc_type, provider);
158 	raexec_setenv(tmp_for_setenv);
159 	g_hash_table_foreach_remove(tmp_for_setenv, let_remove_eachitem, NULL);
160 	g_hash_table_destroy(tmp_for_setenv);
161 
162 	/* let this log show only high loglevel. */
163 	inherit_debuglevel = getenv(HADEBUGVAL);
164 	if ((inherit_debuglevel != NULL) && (atoi(inherit_debuglevel) > 1)) {
165 		params_gstring = g_string_new("");
166 		hash_to_str(params, params_gstring);
167 		cl_log(LOG_DEBUG, "RA instance %s executing: OCF::%s %s. Parameters: "
168 			"{%s}", rsc_id, rsc_type, op_type, params_gstring->str);
169 		g_string_free(params_gstring, TRUE);
170 	}
171 
172 	closefiles(); /* don't leak open files */
173 	/* execute the RA */
174 	execl(ra_pathname, ra_pathname, op_type, (const char *)NULL);
175 	/* oops, exec failed */
176 	save_errno = errno; /* cl_perror may change errno */
177 	cl_perror("(%s:%s:%d) execl failed for %s"
178 		  , __FILE__, __FUNCTION__, __LINE__, ra_pathname);
179 	errno = save_errno;
180 	exit(get_failed_exec_rc());
181 }
182 
183 static uniform_ret_execra_t
map_ra_retvalue(int ret_execra,const char * op_type,const char * std_output)184 map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
185 {
186 	/* Because the UNIFORM_RET_EXECRA is compatible with OCF standard,
187          * no actual mapping except validating, which ensure the return code
188          * will be in the range 0 to 7. Too strict?
189          */
190         if (ret_execra < 0 || ret_execra > 9) {
191                 cl_log(LOG_WARNING, "mapped the invalid return code %d."
192                         , ret_execra);
193                 ret_execra = EXECRA_UNKNOWN_ERROR;
194         }
195 	return ret_execra;
196 }
197 
198 static gint
compare_str(gconstpointer a,gconstpointer b)199 compare_str(gconstpointer a, gconstpointer b)
200 {
201 	return strncmp(a,b,RA_MAX_NAME_LENGTH);
202 }
203 
204 static int
get_resource_list(GList ** rsc_info)205 get_resource_list(GList ** rsc_info)
206 {
207 	struct dirent **namelist;
208 	GList* item;
209 	int file_num;
210 	char subdir[FILENAME_MAX+1];
211 
212 	if ( rsc_info == NULL ) {
213 		cl_log(LOG_ERR, "Parameter error: get_resource_list");
214 		return -2;
215 	}
216 
217 	if ( *rsc_info != NULL ) {
218 		cl_log(LOG_ERR, "Parameter error: get_resource_list."\
219 			"will cause memory leak.");
220 		*rsc_info = NULL;
221 	}
222 	file_num = scandir(RA_PATH, &namelist, NULL, alphasort);
223 	if (file_num < 0) {
224 		return -2;
225 	}
226 	while (file_num--) {
227 		GList* ra_subdir = NULL;
228 		struct stat prop;
229 		if ('.' == namelist[file_num]->d_name[0]) {
230 			free(namelist[file_num]);
231 			continue;
232 		}
233 
234 		snprintf(subdir,FILENAME_MAX,"%s/%s",
235 			 RA_PATH, namelist[file_num]->d_name);
236 
237 		if (stat(subdir, &prop) == -1) {
238 			cl_perror("%s:%s:%d: stat failed for %s"
239 				  , __FILE__, __FUNCTION__, __LINE__, subdir);
240 			free(namelist[file_num]);
241 			continue;
242 		} else if (!S_ISDIR(prop.st_mode)) {
243 			free(namelist[file_num]);
244 			continue;
245 		}
246 
247 		get_runnable_list(subdir,&ra_subdir);
248 
249 		merge_string_list(rsc_info,ra_subdir);
250 
251 		while (NULL != (item = g_list_first(ra_subdir))) {
252 			ra_subdir = g_list_remove_link(ra_subdir, item);
253 			g_free(item->data);
254 			g_list_free_1(item);
255 		}
256 
257 		free(namelist[file_num]);
258 	}
259 	free(namelist);
260 
261 	return 0;
262 }
263 
264 static void
merge_string_list(GList ** old,GList * new)265 merge_string_list(GList** old, GList* new)
266 {
267 	GList* item = NULL;
268 	char* newitem;
269 	for( item=g_list_first(new); NULL!=item; item=g_list_next(item)){
270 		if (!g_list_find_custom(*old, item->data,compare_str)){
271 			newitem = g_strndup(item->data,RA_MAX_NAME_LENGTH);
272 			*old = g_list_append(*old, newitem);
273 		}
274 	}
275 }
276 
277 static int
get_provider_list(const char * ra_type,GList ** providers)278 get_provider_list(const char* ra_type, GList ** providers)
279 {
280 	int ret;
281 	ret = get_providers(RA_PATH, ra_type, providers);
282 	if (0>ret) {
283 		cl_log(LOG_ERR, "scandir failed in OCF RA plugin");
284 	}
285 	return ret;
286 }
287 
288 static char*
get_resource_meta(const char * rsc_type,const char * provider)289 get_resource_meta(const char* rsc_type, const char* provider)
290 {
291 	const int BUFF_LEN=4096;
292 	int read_len = 0;
293 	char buff[BUFF_LEN];
294 	char* data = NULL;
295 	GString* g_str_tmp = NULL;
296 	char ra_pathname[RA_MAX_NAME_LENGTH];
297 	FILE* file = NULL;
298 	GHashTable * tmp_for_setenv;
299 	struct timespec short_sleep = {0,200000000L}; /*20ms*/
300 
301 	get_ra_pathname(RA_PATH, rsc_type, provider, ra_pathname);
302 
303 	strncat(ra_pathname, " meta-data",RA_MAX_NAME_LENGTH-strlen(ra_pathname)-1);
304 	tmp_for_setenv = g_hash_table_new(g_str_hash, g_str_equal);
305 	add_OCF_env_vars(tmp_for_setenv, "DUMMY_INSTANCE", rsc_type, provider);
306 	raexec_setenv(tmp_for_setenv);
307 	g_hash_table_foreach_remove(tmp_for_setenv, let_remove_eachitem, NULL);
308 	g_hash_table_destroy(tmp_for_setenv);
309 
310 	file = popen(ra_pathname, "r");
311 	if (NULL==file) {
312 		cl_log(LOG_ERR, "%s: popen failed: %s", __FUNCTION__, strerror(errno));
313 		return NULL;
314 	}
315 
316 	g_str_tmp = g_string_new("");
317 	while(!feof(file)) {
318 		read_len = fread(buff, 1, BUFF_LEN - 1, file);
319 		if (0<read_len) {
320 			*(buff+read_len) = '\0';
321 			g_string_append(g_str_tmp, buff);
322 		}
323 		else {
324 			nanosleep(&short_sleep,NULL);
325 		}
326 	}
327 	if( pclose(file) ) {
328 		cl_log(LOG_ERR, "%s: pclose failed: %s", __FUNCTION__, strerror(errno));
329 	}
330 	if (0 == g_str_tmp->len) {
331 		g_string_free(g_str_tmp, TRUE);
332 		return NULL;
333 	}
334 	data = (char*)g_new(char, g_str_tmp->len+1);
335 	data[0] = data[g_str_tmp->len] = 0;
336 	strncpy(data, g_str_tmp->str, g_str_tmp->len);
337 
338 	g_string_free(g_str_tmp, TRUE);
339 
340 	return data;
341 }
342 
343 static void
add_OCF_prefix(GHashTable * env_params,GHashTable * new_env_params)344 add_OCF_prefix(GHashTable * env_params, GHashTable * new_env_params)
345 {
346 	if (env_params) {
347 		g_hash_table_foreach(env_params, add_prefix_foreach,
348 				     new_env_params);
349 	}
350 }
351 
352 static void
add_prefix_foreach(gpointer key,gpointer value,gpointer user_data)353 add_prefix_foreach(gpointer key, gpointer value, gpointer user_data)
354 {
355 	const int MAX_LENGTH_OF_ENV = 128;
356 	int prefix = STRLEN_CONST("OCF_RESKEY_");
357 	GHashTable * new_hashtable = (GHashTable *) user_data;
358 	char * newkey;
359 	int keylen = strnlen((char*)key, MAX_LENGTH_OF_ENV-prefix)+prefix+1;
360 
361 	newkey = g_new(gchar, keylen);
362 	strncpy(newkey, "OCF_RESKEY_", keylen);
363 	strncat(newkey, key, keylen-strlen(newkey)-1);
364 	g_hash_table_insert(new_hashtable, (gpointer)newkey, g_strdup(value));
365 }
366 
367 static void
hash_to_str(GHashTable * params,GString * str)368 hash_to_str(GHashTable * params , GString * str)
369 {
370 	if (params) {
371 		g_hash_table_foreach(params, hash_to_str_foreach, str);
372 	}
373 }
374 
375 static void
hash_to_str_foreach(gpointer key,gpointer value,gpointer user_data)376 hash_to_str_foreach(gpointer key, gpointer value, gpointer user_data)
377 {
378 	char buffer_tmp[60];
379 	GString * str = (GString *)user_data;
380 
381 	snprintf(buffer_tmp, 60, "%s=%s ", (char *)key, (char *)value);
382 	str = g_string_append(str, buffer_tmp);
383 }
384 
385 static gboolean
let_remove_eachitem(gpointer key,gpointer value,gpointer user_data)386 let_remove_eachitem(gpointer key, gpointer value, gpointer user_data)
387 {
388 	g_free(key);
389 	g_free(value);
390 	return TRUE;
391 }
392 
393 static int
raexec_setenv(GHashTable * env_params)394 raexec_setenv(GHashTable * env_params)
395 {
396         if (env_params) {
397         	g_hash_table_foreach(env_params, set_env, NULL);
398         }
399         return 0;
400 }
401 
402 static void
set_env(gpointer key,gpointer value,gpointer user_data)403 set_env(gpointer key, gpointer value, gpointer user_data)
404 {
405        if (setenv(key, value, 1) != 0) {
406 		cl_log(LOG_ERR, "setenv failed in raexecocf.");
407 	}
408 }
409 
410 static int
get_providers(const char * class_path,const char * ra_type,GList ** providers)411 get_providers(const char* class_path, const char* ra_type, GList ** providers)
412 {
413 	struct dirent **namelist;
414 	int file_num;
415 
416 	if ( providers == NULL ) {
417 		cl_log(LOG_ERR, "Parameter error: get_providers");
418 		return -2;
419 	}
420 
421 	if ( *providers != NULL ) {
422 		cl_log(LOG_ERR, "Parameter error: get_providers."\
423 			"will cause memory leak.");
424 		*providers = NULL;
425 	}
426 
427 	file_num = scandir(class_path, &namelist, 0, alphasort);
428 	if (file_num < 0) {
429 		return -2;
430 	}else{
431 		char tmp_buffer[FILENAME_MAX+1];
432 		struct stat prop;
433 
434 		while (file_num--) {
435 			if ('.' == namelist[file_num]->d_name[0]) {
436 				free(namelist[file_num]);
437 				continue;
438 			}
439 			snprintf(tmp_buffer,FILENAME_MAX,"%s/%s",
440 				 class_path, namelist[file_num]->d_name);
441 			stat(tmp_buffer, &prop);
442 			if (!S_ISDIR(prop.st_mode)) {
443 				free(namelist[file_num]);
444 				continue;
445 			}
446 
447 			snprintf(tmp_buffer,FILENAME_MAX,"%s/%s/%s",
448 				 class_path, namelist[file_num]->d_name, ra_type);
449 
450 			if ( filtered(tmp_buffer) == TRUE ) {
451 				*providers = g_list_append(*providers,
452 					g_strdup(namelist[file_num]->d_name));
453 			}
454 			free(namelist[file_num]);
455 		}
456 		free(namelist);
457 	}
458 	return g_list_length(*providers);
459 }
460 
461 static void
add_OCF_env_vars(GHashTable * env,const char * rsc_id,const char * rsc_type,const char * provider)462 add_OCF_env_vars(GHashTable * env, const char * rsc_id,
463 	         const char * rsc_type, const char * provider)
464 {
465 	if ( env == NULL ) {
466 		cl_log(LOG_WARNING, "env should not be a NULL pointer.");
467 		return;
468 	}
469 
470 	g_hash_table_insert(env, g_strdup("OCF_RA_VERSION_MAJOR"),
471 			    g_strdup("1"));
472 	g_hash_table_insert(env, g_strdup("OCF_RA_VERSION_MINOR"),
473 			    g_strdup("0"));
474 	g_hash_table_insert(env, g_strdup("OCF_ROOT"),
475 			    g_strdup(OCF_ROOT_DIR));
476 
477 	if ( rsc_id != NULL ) {
478 		g_hash_table_insert(env, g_strdup("OCF_RESOURCE_INSTANCE"),
479 				    g_strdup(rsc_id));
480 	}
481 
482 	/* Currently the rsc_type=="the filename of the RA script/executable",
483 	 * It seems always correct even in the furture. ;-)
484 	 */
485 	if ( rsc_type != NULL ) {
486 		g_hash_table_insert(env, g_strdup("OCF_RESOURCE_TYPE"),
487 				    g_strdup(rsc_type));
488 	}
489 
490 	/* Notes: this is not added to specification yet. Sept 10,2004 */
491 	if ( provider != NULL ) {
492 		g_hash_table_insert(env, g_strdup("OCF_RESOURCE_PROVIDER"),
493 			    	    g_strdup(provider));
494 	}
495 }
496 
497