PageRenderTime 64ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/common/plugin.c

https://github.com/cfenoy/slurm
C | 465 lines | 334 code | 52 blank | 79 comment | 85 complexity | 83ba09bf8b98dbd71664f6ef45878a2b MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0
  1. /*****************************************************************************\
  2. * plugin.h - plugin architecture implementation.
  3. *****************************************************************************
  4. * Copyright (C) 2002-2007 The Regents of the University of California.
  5. * Copyright (C) 2008-2009 Lawrence Livermore National Security.
  6. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  7. * Written by Jay Windley <jwindley@lnxi.com>.
  8. * CODE-OCEC-09-009. All rights reserved.
  9. * Portions Copyright (C) 2012 SchedMD LLC.
  10. * Written by Danny Auble <da@schedmd.com>
  11. *
  12. * This file is part of SLURM, a resource management program.
  13. * For details, see <http://www.schedmd.com/slurmdocs/>.
  14. * Please also read the included file: DISCLAIMER.
  15. *
  16. * SLURM is free software; you can redistribute it and/or modify it under
  17. * the terms of the GNU General Public License as published by the Free
  18. * Software Foundation; either version 2 of the License, or (at your option)
  19. * any later version.
  20. *
  21. * In addition, as a special exception, the copyright holders give permission
  22. * to link the code of portions of this program with the OpenSSL library under
  23. * certain conditions as described in each individual source file, and
  24. * distribute linked combinations including the two. You must obey the GNU
  25. * General Public License in all respects for all of the code used other than
  26. * OpenSSL. If you modify file(s) with this exception, you may extend this
  27. * exception to your version of the file(s), but you are not obligated to do
  28. * so. If you do not wish to do so, delete this exception statement from your
  29. * version. If you delete this exception statement from all source files in
  30. * the program, then also delete it here.
  31. *
  32. * SLURM is distributed in the hope that it will be useful, but WITHOUT ANY
  33. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  34. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  35. * details.
  36. *
  37. * You should have received a copy of the GNU General Public License along
  38. * with SLURM; if not, write to the Free Software Foundation, Inc.,
  39. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  40. \*****************************************************************************/
  41. #if HAVE_CONFIG_H
  42. # include "config.h"
  43. #endif
  44. #include <errno.h>
  45. #include <sys/types.h>
  46. #include <stdio.h>
  47. #include <dlfcn.h> /* don't know if there's an autoconf for this. */
  48. #include <string.h>
  49. #include "src/common/xmalloc.h"
  50. #include "src/common/log.h"
  51. #include "src/common/plugrack.h"
  52. #include "src/common/xstring.h"
  53. #include "src/common/slurm_protocol_api.h"
  54. #include "slurm/slurm_errno.h"
  55. # if HAVE_UNISTD_H
  56. # include <unistd.h>
  57. # endif /* HAVE_UNISTD_H */
  58. # if HAVE_SYS_TYPES_H
  59. # include <sys/types.h>
  60. # endif
  61. # if HAVE_SYS_STAT_H
  62. # include <sys/stat.h>
  63. # endif
  64. # if HAVE_STDLIB_H
  65. # include <stdlib.h>
  66. # endif
  67. strong_alias(plugin_get_syms, slurm_plugin_get_syms);
  68. strong_alias(plugin_load_and_link, slurm_plugin_load_and_link);
  69. strong_alias(plugin_strerror, slurm_plugin_strerror);
  70. strong_alias(plugin_unload, slurm_plugin_unload);
  71. /* dlerror() on AIX sometimes fails, revert to strerror() as needed */
  72. static char *_dlerror(void)
  73. {
  74. int error_code = errno;
  75. char *rc = dlerror();
  76. if ((rc == NULL) || (rc[0] == '\0'))
  77. rc = strerror(error_code);
  78. return rc;
  79. }
  80. const char * plugin_strerror(plugin_err_t e)
  81. {
  82. switch (e) {
  83. case EPLUGIN_SUCCESS:
  84. return ("Success");
  85. case EPLUGIN_NOTFOUND:
  86. return ("Plugin file not found");
  87. case EPLUGIN_ACCESS_ERROR:
  88. return ("Plugin access denied");
  89. case EPLUGIN_DLOPEN_FAILED:
  90. return ("Dlopen of plugin file failed");
  91. case EPLUGIN_INIT_FAILED:
  92. return ("Plugin init() callback failed");
  93. case EPLUGIN_MISSING_NAME:
  94. return ("Plugin name/type/version symbol missing");
  95. case EPLUGIN_MISSING_SYMBOL:
  96. return ("Plugin missing a required symbol use "
  97. "debug3 to see");
  98. }
  99. return ("Unknown error");
  100. }
  101. int
  102. plugin_peek( const char *fq_path,
  103. char *plugin_type,
  104. const size_t type_len,
  105. uint32_t *plugin_version )
  106. {
  107. plugin_handle_t plug;
  108. char *type;
  109. uint32_t *version;
  110. plug = dlopen( fq_path, RTLD_LAZY );
  111. if ( plug == NULL ) {
  112. debug3( "plugin_peek: dlopen(%s): %s", fq_path, _dlerror() );
  113. return SLURM_ERROR;
  114. }
  115. if ( ( type = dlsym( plug, PLUGIN_TYPE ) ) != NULL ) {
  116. if ( plugin_type != NULL ) {
  117. strncpy( plugin_type, type, type_len );
  118. }
  119. } else {
  120. dlclose( plug );
  121. /* could be vestigial library, don't treat as an error */
  122. verbose( "%s: not a SLURM plugin", fq_path );
  123. return SLURM_ERROR;
  124. }
  125. if ( ( version = (uint32_t *) dlsym( plug, PLUGIN_VERSION ) ) != NULL ) {
  126. if ( plugin_version != NULL ) {
  127. *plugin_version = *version;
  128. }
  129. } else {
  130. dlclose( plug );
  131. /* could be vestigial library, don't treat as an error */
  132. verbose( "%s: not a SLURM plugin", fq_path );
  133. return SLURM_ERROR;
  134. }
  135. dlclose( plug );
  136. return SLURM_SUCCESS;
  137. }
  138. plugin_err_t
  139. plugin_load_from_file(plugin_handle_t *p, const char *fq_path)
  140. {
  141. plugin_handle_t plug;
  142. int (*init)(void);
  143. *p = PLUGIN_INVALID_HANDLE;
  144. /*
  145. * Check for file existence and access permissions
  146. */
  147. if (access(fq_path, R_OK) < 0) {
  148. if (errno == ENOENT)
  149. return EPLUGIN_NOTFOUND;
  150. else
  151. return EPLUGIN_ACCESS_ERROR;
  152. }
  153. /*
  154. * Try to open the shared object.
  155. *
  156. * Use RTLD_LAZY to allow plugins to use symbols that may be
  157. * defined in only one slurm entity (e.g. srun and not slurmd),
  158. * when the use of that symbol is restricted to within the
  159. * entity from which it is available. (i.e. srun symbols are only
  160. * used in the context of srun, not slurmd.)
  161. *
  162. */
  163. plug = dlopen(fq_path, RTLD_LAZY);
  164. if (plug == NULL) {
  165. error("plugin_load_from_file: dlopen(%s): %s",
  166. fq_path,
  167. _dlerror());
  168. return EPLUGIN_DLOPEN_FAILED;
  169. }
  170. /* Now see if our required symbols are defined. */
  171. if ((dlsym(plug, PLUGIN_NAME) == NULL) ||
  172. (dlsym(plug, PLUGIN_TYPE) == NULL) ||
  173. (dlsym(plug, PLUGIN_VERSION) == NULL)) {
  174. dlclose (plug);
  175. return EPLUGIN_MISSING_NAME;
  176. }
  177. /*
  178. * Now call its init() function, if present. If the function
  179. * returns nonzero, unload the plugin and signal an error.
  180. */
  181. if ((init = dlsym(plug, "init")) != NULL) {
  182. if ((*init)() != 0) {
  183. dlclose(plug);
  184. return EPLUGIN_INIT_FAILED;
  185. }
  186. }
  187. *p = plug;
  188. return EPLUGIN_SUCCESS;
  189. }
  190. plugin_handle_t
  191. plugin_load_and_link(const char *type_name, int n_syms,
  192. const char *names[], void *ptrs[])
  193. {
  194. plugin_handle_t plug = PLUGIN_INVALID_HANDLE;
  195. struct stat st;
  196. char *head=NULL, *dir_array=NULL, *so_name = NULL,
  197. *file_name=NULL;
  198. int i=0;
  199. plugin_err_t err = EPLUGIN_NOTFOUND;
  200. if (!type_name)
  201. return plug;
  202. #if defined(__CYGWIN__)
  203. so_name = xstrdup_printf("%s.dll", type_name);
  204. #else
  205. so_name = xstrdup_printf("%s.so", type_name);
  206. #endif
  207. while(so_name[i]) {
  208. if(so_name[i] == '/')
  209. so_name[i] = '_';
  210. i++;
  211. }
  212. if(!(dir_array = slurm_get_plugin_dir())) {
  213. error("plugin_load_and_link: No plugin dir given");
  214. xfree(so_name);
  215. return plug;
  216. }
  217. head = dir_array;
  218. for (i=0; ; i++) {
  219. bool got_colon = 0;
  220. if (dir_array[i] == ':') {
  221. dir_array[i] = '\0';
  222. got_colon = 1;
  223. } else if(dir_array[i] != '\0')
  224. continue;
  225. file_name = xstrdup_printf("%s/%s", head, so_name);
  226. debug3("Trying to load plugin %s", file_name);
  227. if ((stat(file_name, &st) < 0) || (!S_ISREG(st.st_mode))) {
  228. debug4("%s: Does not exist or not a regular file.",
  229. file_name);
  230. xfree(file_name);
  231. err = EPLUGIN_NOTFOUND;
  232. } else {
  233. if((err = plugin_load_from_file(&plug, file_name))
  234. == EPLUGIN_SUCCESS) {
  235. if (plugin_get_syms(plug, n_syms,
  236. names, ptrs) >=
  237. n_syms) {
  238. debug3("Success.");
  239. xfree(file_name);
  240. break;
  241. } else {
  242. err = EPLUGIN_MISSING_SYMBOL;
  243. plug = PLUGIN_INVALID_HANDLE;
  244. }
  245. } else
  246. plug = PLUGIN_INVALID_HANDLE;
  247. xfree(file_name);
  248. }
  249. if (got_colon) {
  250. head = dir_array + i + 1;
  251. } else
  252. break;
  253. }
  254. xfree(dir_array);
  255. xfree(so_name);
  256. errno = err;
  257. return plug;
  258. }
  259. /*
  260. * Must test plugin validity before doing dlopen() and dlsym()
  261. * operations because some implementations of these functions
  262. * crash if the library handle is not valid.
  263. */
  264. void
  265. plugin_unload( plugin_handle_t plug )
  266. {
  267. void (*fini)(void);
  268. if ( plug != PLUGIN_INVALID_HANDLE ) {
  269. if ( ( fini = dlsym( plug, "fini" ) ) != NULL ) {
  270. (*fini)();
  271. }
  272. (void) dlclose( plug );
  273. }
  274. }
  275. void *
  276. plugin_get_sym( plugin_handle_t plug, const char *name )
  277. {
  278. if ( plug != PLUGIN_INVALID_HANDLE )
  279. return dlsym( plug, name );
  280. else
  281. return NULL;
  282. }
  283. const char *
  284. plugin_get_name( plugin_handle_t plug )
  285. {
  286. if ( plug != PLUGIN_INVALID_HANDLE )
  287. return (const char *) dlsym( plug, PLUGIN_NAME );
  288. else
  289. return NULL;
  290. }
  291. const char *
  292. plugin_get_type( plugin_handle_t plug )
  293. {
  294. if ( plug != PLUGIN_INVALID_HANDLE )
  295. return (const char *) dlsym( plug, PLUGIN_TYPE );
  296. else
  297. return NULL;
  298. }
  299. uint32_t
  300. plugin_get_version( plugin_handle_t plug )
  301. {
  302. uint32_t *ptr;
  303. if ( plug == PLUGIN_INVALID_HANDLE ) return 0;
  304. ptr = (uint32_t *) dlsym( plug, PLUGIN_VERSION );
  305. return ptr ? *ptr : 0;
  306. }
  307. int
  308. plugin_get_syms( plugin_handle_t plug,
  309. int n_syms,
  310. const char *names[],
  311. void *ptrs[] )
  312. {
  313. int i, count;
  314. count = 0;
  315. for ( i = 0; i < n_syms; ++i ) {
  316. ptrs[ i ] = dlsym( plug, names[ i ] );
  317. if ( ptrs[ i ] )
  318. ++count;
  319. else
  320. debug3("Couldn't find sym '%s' in the plugin",
  321. names[ i ]);
  322. }
  323. return count;
  324. }
  325. /*
  326. * Create a priority context
  327. */
  328. extern plugin_context_t *plugin_context_create(
  329. const char *plugin_type, const char *uler_type,
  330. void *ptrs[], const char *names[], size_t names_size)
  331. {
  332. plugin_context_t *c;
  333. int n_names;
  334. if (!uler_type) {
  335. debug3("plugin_context_create: no uler type");
  336. return NULL;
  337. } else if (!plugin_type) {
  338. debug3("plugin_context_create: no plugin type");
  339. return NULL;
  340. } else if (!names) {
  341. error("plugin_context_create: no symbols given for plugin %s",
  342. plugin_type);
  343. return NULL;
  344. } else if (!ptrs) {
  345. error("plugin_context_create: no ptrs given for plugin %s",
  346. plugin_type);
  347. return NULL;
  348. }
  349. c = xmalloc(sizeof(plugin_context_t));
  350. c->type = xstrdup(uler_type);
  351. c->cur_plugin = PLUGIN_INVALID_HANDLE;
  352. n_names = names_size / sizeof(char *);
  353. /* Find the correct plugin. */
  354. c->cur_plugin = plugin_load_and_link(c->type, n_names, names, ptrs);
  355. if (c->cur_plugin != PLUGIN_INVALID_HANDLE)
  356. return c;
  357. if (errno != EPLUGIN_NOTFOUND) {
  358. error("Couldn't load specified plugin name for %s: %s",
  359. c->type, plugin_strerror(errno));
  360. goto fail;
  361. }
  362. error("Couldn't find the specified plugin name for %s "
  363. "looking at all files",
  364. c->type);
  365. /* Get plugin list. */
  366. if (!c->plugin_list) {
  367. char *plugin_dir;
  368. c->plugin_list = plugrack_create();
  369. if (!c->plugin_list) {
  370. error("cannot create plugin manager");
  371. goto fail;
  372. }
  373. plugrack_set_major_type(c->plugin_list, plugin_type);
  374. plugrack_set_paranoia(
  375. c->plugin_list, PLUGRACK_PARANOIA_NONE, 0);
  376. plugin_dir = slurm_get_plugin_dir();
  377. plugrack_read_dir(c->plugin_list, plugin_dir);
  378. xfree(plugin_dir);
  379. }
  380. c->cur_plugin = plugrack_use_by_type(c->plugin_list, c->type);
  381. if (c->cur_plugin == PLUGIN_INVALID_HANDLE) {
  382. error("cannot find %s plugin for %s", plugin_type, c->type);
  383. goto fail;
  384. }
  385. /* Dereference the API. */
  386. if (plugin_get_syms(c->cur_plugin, n_names, names, ptrs) < n_names) {
  387. error("incomplete %s plugin detected", plugin_type);
  388. goto fail;
  389. }
  390. return c;
  391. fail:
  392. plugin_context_destroy(c);
  393. return NULL;
  394. }
  395. /*
  396. * Destroy a context
  397. */
  398. extern int plugin_context_destroy(plugin_context_t *c)
  399. {
  400. int rc = SLURM_SUCCESS;
  401. /*
  402. * Must check return code here because plugins might still
  403. * be loaded and active.
  404. */
  405. if (c->plugin_list) {
  406. if (plugrack_destroy(c->plugin_list) != SLURM_SUCCESS)
  407. rc = SLURM_ERROR;
  408. } else
  409. plugin_unload(c->cur_plugin);
  410. xfree(c->type);
  411. xfree(c);
  412. return rc;
  413. }