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