kplugin.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <stdint.h>
  4. #include <string.h>
  5. #include <assert.h>
  6. #include <dlfcn.h>
  7. #include <faux/str.h>
  8. #include <faux/list.h>
  9. #include <faux/conv.h>
  10. #include <faux/error.h>
  11. #include <klish/khelper.h>
  12. #include <klish/kplugin.h>
  13. #include <klish/ksym.h>
  14. struct kplugin_s {
  15. char *name;
  16. char *id;
  17. char *file;
  18. bool_t global;
  19. char *conf;
  20. uint8_t major;
  21. uint8_t minor;
  22. void *dlhan; // dlopen() handler
  23. void *init_fn;
  24. void *fini_fn;
  25. void *udata; // User data
  26. faux_list_t *syms;
  27. };
  28. // Simple methods
  29. // Name
  30. KGET_STR(plugin, name);
  31. KSET_STR_ONCE(plugin, name);
  32. // ID
  33. KGET_STR(plugin, id);
  34. KSET_STR(plugin, id);
  35. // File
  36. KGET_STR(plugin, file);
  37. KSET_STR(plugin, file);
  38. // Global
  39. KGET_BOOL(plugin, global);
  40. KSET_BOOL(plugin, global);
  41. // Conf
  42. KGET_STR(plugin, conf);
  43. KSET_STR(plugin, conf);
  44. // Version major number
  45. KGET(plugin, uint8_t, major);
  46. KSET(plugin, uint8_t, major);
  47. // Version minor number
  48. KGET(plugin, uint8_t, minor);
  49. KSET(plugin, uint8_t, minor);
  50. // User data
  51. KGET(plugin, void *, udata);
  52. KSET(plugin, void *, udata);
  53. // COMMAND list
  54. static KCMP_NESTED(plugin, sym, name);
  55. static KCMP_NESTED_BY_KEY(plugin, sym, name);
  56. KADD_NESTED(plugin, sym);
  57. KFIND_NESTED(plugin, sym);
  58. static kplugin_t *kplugin_new_empty(void)
  59. {
  60. kplugin_t *plugin = NULL;
  61. plugin = faux_zmalloc(sizeof(*plugin));
  62. assert(plugin);
  63. if (!plugin)
  64. return NULL;
  65. // Initialize
  66. plugin->name = NULL;
  67. plugin->id = NULL;
  68. plugin->file = NULL;
  69. plugin->global = BOOL_FALSE;
  70. plugin->conf = NULL;
  71. plugin->major = 0;
  72. plugin->minor = 0;
  73. plugin->dlhan = NULL;
  74. plugin->init_fn = NULL;
  75. plugin->fini_fn = NULL;
  76. plugin->udata = NULL;
  77. // SYM list
  78. plugin->syms = faux_list_new(FAUX_LIST_SORTED, FAUX_LIST_UNIQUE,
  79. kplugin_sym_compare, kplugin_sym_kcompare,
  80. (void (*)(void *))ksym_free);
  81. assert(plugin->syms);
  82. return plugin;
  83. }
  84. kplugin_t *kplugin_new(const iplugin_t *info, kplugin_error_e *error)
  85. {
  86. kplugin_t *plugin = NULL;
  87. plugin = kplugin_new_empty();
  88. assert(plugin);
  89. if (!plugin) {
  90. if (error)
  91. *error = KPLUGIN_ERROR_ALLOC;
  92. return NULL;
  93. }
  94. if (!info)
  95. return plugin;
  96. if (!kplugin_parse(plugin, info, error)) {
  97. kplugin_free(plugin);
  98. return NULL;
  99. }
  100. return plugin;
  101. }
  102. void kplugin_free(kplugin_t *plugin)
  103. {
  104. if (!plugin)
  105. return;
  106. faux_str_free(plugin->name);
  107. faux_str_free(plugin->id);
  108. faux_str_free(plugin->file);
  109. faux_str_free(plugin->conf);
  110. faux_list_free(plugin->syms);
  111. if (plugin->dlhan)
  112. dlclose(plugin->dlhan);
  113. faux_free(plugin);
  114. }
  115. const char *kplugin_strerror(kplugin_error_e error)
  116. {
  117. const char *str = NULL;
  118. switch (error) {
  119. case KPLUGIN_ERROR_OK:
  120. str = "Ok";
  121. break;
  122. case KPLUGIN_ERROR_INTERNAL:
  123. str = "Internal error";
  124. break;
  125. case KPLUGIN_ERROR_ALLOC:
  126. str = "Memory allocation error";
  127. break;
  128. case KPLUGIN_ERROR_ATTR_NAME:
  129. str = "Illegal 'name' attribute";
  130. break;
  131. case KPLUGIN_ERROR_ATTR_ID:
  132. str = "Illegal 'id' attribute";
  133. break;
  134. case KPLUGIN_ERROR_ATTR_FILE:
  135. str = "Illegal 'file' attribute";
  136. break;
  137. case KPLUGIN_ERROR_ATTR_GLOBAL:
  138. str = "Illegal 'global' attribute";
  139. break;
  140. case KPLUGIN_ERROR_ATTR_CONF:
  141. str = "Illegal conf";
  142. break;
  143. default:
  144. str = "Unknown error";
  145. break;
  146. }
  147. return str;
  148. }
  149. bool_t kplugin_parse(kplugin_t *plugin, const iplugin_t *info, kplugin_error_e *error)
  150. {
  151. // Name [mandatory]
  152. if (faux_str_is_empty(info->name)) {
  153. if (error)
  154. *error = KPLUGIN_ERROR_ATTR_NAME;
  155. return BOOL_FALSE;
  156. } else {
  157. if (!kplugin_set_name(plugin, info->name)) {
  158. if (error)
  159. *error = KPLUGIN_ERROR_ATTR_NAME;
  160. return BOOL_FALSE;
  161. }
  162. }
  163. // ID
  164. if (!faux_str_is_empty(info->id)) {
  165. if (!kplugin_set_id(plugin, info->id)) {
  166. if (error)
  167. *error = KPLUGIN_ERROR_ATTR_ID;
  168. return BOOL_FALSE;
  169. }
  170. }
  171. // File
  172. if (!faux_str_is_empty(info->file)) {
  173. if (!kplugin_set_file(plugin, info->file)) {
  174. if (error)
  175. *error = KPLUGIN_ERROR_ATTR_FILE;
  176. return BOOL_FALSE;
  177. }
  178. }
  179. // Global
  180. if (!faux_str_is_empty(info->global)) {
  181. bool_t b = BOOL_FALSE;
  182. if (!faux_conv_str2bool(info->global, &b) ||
  183. !kplugin_set_global(plugin, b)) {
  184. if (error)
  185. *error = KPLUGIN_ERROR_ATTR_GLOBAL;
  186. return BOOL_FALSE;
  187. }
  188. }
  189. // Conf
  190. if (!faux_str_is_empty(info->conf)) {
  191. if (!kplugin_set_conf(plugin, info->conf)) {
  192. if (error)
  193. *error = KPLUGIN_ERROR_ATTR_CONF;
  194. return BOOL_FALSE;
  195. }
  196. }
  197. return BOOL_TRUE;
  198. }
  199. kplugin_t *kplugin_from_iplugin(iplugin_t *iplugin, faux_error_t *error_stack)
  200. {
  201. kplugin_t *kplugin = NULL;
  202. kplugin_error_e kplugin_error = KPLUGIN_ERROR_OK;
  203. kplugin = kplugin_new(iplugin, &kplugin_error);
  204. if (!kplugin) {
  205. faux_error_sprintf(error_stack, "PLUGIN \"%s\": %s",
  206. iplugin->name ? iplugin->name : "(null)",
  207. kplugin_strerror(kplugin_error));
  208. return NULL;
  209. }
  210. return kplugin;
  211. }
  212. bool_t kplugin_load(kplugin_t *plugin)
  213. {
  214. char *file_name = NULL;
  215. char *init_name = NULL;
  216. char *fini_name = NULL;
  217. char *major_name = NULL;
  218. char *minor_name = NULL;
  219. int flag = RTLD_NOW;
  220. const char *id = NULL;
  221. bool_t retcode = BOOL_FALSE;
  222. uint8_t *ver = NULL;
  223. assert(plugin);
  224. if (!plugin)
  225. return BOOL_FALSE;
  226. if (kplugin_id(plugin))
  227. id = kplugin_id(plugin);
  228. else
  229. id = kplugin_name(plugin);
  230. // Shared object file name
  231. if (kplugin_file(plugin))
  232. file_name = faux_str_dup(kplugin_file(plugin));
  233. else
  234. file_name = faux_str_sprintf(KPLUGIN_SONAME_FMT, id);
  235. // Symbol names
  236. major_name = faux_str_sprintf(KPLUGIN_MAJOR_FMT, id);
  237. minor_name = faux_str_sprintf(KPLUGIN_MINOR_FMT, id);
  238. init_name = faux_str_sprintf(KPLUGIN_INIT_FMT, id);
  239. fini_name = faux_str_sprintf(KPLUGIN_FINI_FMT, id);
  240. // SO flags
  241. if (kplugin_global(plugin))
  242. flag |= RTLD_GLOBAL;
  243. else
  244. flag |= RTLD_LOCAL;
  245. // Open shared object
  246. plugin->dlhan = dlopen(file_name, flag);
  247. if (!plugin->dlhan) {
  248. // fprintf(stderr, "Error: Can't open plugin \"%s\": %s\n",
  249. // this->name, dlerror());
  250. goto err;
  251. }
  252. // Get plugin version
  253. ver = (uint8_t *)dlsym(plugin->dlhan, major_name);
  254. if (!ver)
  255. goto err;
  256. kplugin_set_major(plugin, *ver);
  257. ver = (uint8_t *)dlsym(plugin->dlhan, minor_name);
  258. if (!ver)
  259. goto err;
  260. kplugin_set_minor(plugin, *ver);
  261. // Get plugin init function
  262. plugin->init_fn = dlsym(plugin->dlhan, init_name);
  263. if (!plugin->init_fn) {
  264. // fprintf(stderr, "Error: Can't get plugin \"%s\" init function: %s\n",
  265. // this->name, dlerror());
  266. goto err;
  267. }
  268. // Get plugin fini function
  269. plugin->fini_fn = dlsym(plugin->dlhan, fini_name);
  270. retcode = BOOL_TRUE;
  271. err:
  272. faux_str_free(file_name);
  273. faux_str_free(major_name);
  274. faux_str_free(minor_name);
  275. faux_str_free(init_name);
  276. faux_str_free(fini_name);
  277. if (!retcode && plugin->dlhan)
  278. dlclose(plugin->dlhan);
  279. return retcode;
  280. }