statechange.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  3. * Copyright (C) 2005 Red Hat, Inc.
  4. * All rights reserved.
  5. *
  6. * License: GPL (version 3 or any later version).
  7. * See LICENSE for details.
  8. * END COPYRIGHT BLOCK **/
  9. #ifdef HAVE_CONFIG_H
  10. #include <config.h>
  11. #endif
  12. /* plugin which provides a callback mechanism for state changes in the DS */
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include "portable.h"
  16. #include "slapi-plugin.h"
  17. #include "slapi-private.h"
  18. #include "statechange.h"
  19. #include <sys/stat.h>
  20. /* the circular list of systems to notify */
  21. typedef struct _statechange_notify
  22. {
  23. char *caller_id;
  24. char *dn;
  25. char *filter;
  26. Slapi_Filter *realfilter;
  27. notify_callback func;
  28. void *caller_data;
  29. struct _statechange_notify *next;
  30. struct _statechange_notify *prev;
  31. } SCNotify;
  32. static SCNotify *head; /* a place to start in the list */
  33. #define SCN_PLUGIN_SUBSYSTEM "statechange-plugin" /* used for logging */
  34. static void *api[5];
  35. static Slapi_Mutex *buffer_lock = 0;
  36. static PRUint64 g_plugin_started = 0;
  37. /*
  38. * We can not fully use the built in plugin counter in the statechange plugin,
  39. * so we have to use our own.
  40. */
  41. static Slapi_Counter *op_counter = NULL;
  42. /* other function prototypes */
  43. int statechange_init(Slapi_PBlock *pb);
  44. static int statechange_start(Slapi_PBlock *pb);
  45. static int statechange_close(Slapi_PBlock *pb);
  46. static int statechange_post_op(Slapi_PBlock *pb, int modtype);
  47. static int statechange_mod_post_op(Slapi_PBlock *pb);
  48. static int statechange_modrdn_post_op(Slapi_PBlock *pb);
  49. static int statechange_add_post_op(Slapi_PBlock *pb);
  50. static int statechange_delete_post_op(Slapi_PBlock *pb);
  51. static int _statechange_register(char *caller_id, char *dn, char *filter, void *caller_data, notify_callback func);
  52. static void *_statechange_unregister(char *dn, char *filter, notify_callback func);
  53. static void _statechange_unregister_all(char *caller_id, caller_data_free_callback);
  54. static void _statechange_vattr_cache_invalidator_callback(Slapi_Entry *e, char *dn, int modtype, Slapi_PBlock *pb, void *caller_data);
  55. static SCNotify *statechange_find_notify(char *dn, char *filter, notify_callback func);
  56. static Slapi_PluginDesc pdesc = {"statechange", VENDOR, DS_PACKAGE_VERSION,
  57. "state change notification service plugin"};
  58. /*
  59. statechange_init
  60. --------
  61. adds our callbacks to the list
  62. */
  63. int
  64. statechange_init(Slapi_PBlock *pb)
  65. {
  66. int ret = SLAPI_PLUGIN_SUCCESS;
  67. Slapi_Entry *plugin_entry = NULL;
  68. char *plugin_type = NULL;
  69. int postadd = SLAPI_PLUGIN_POST_ADD_FN;
  70. int postmod = SLAPI_PLUGIN_POST_MODIFY_FN;
  71. int postmdn = SLAPI_PLUGIN_POST_MODRDN_FN;
  72. int postdel = SLAPI_PLUGIN_POST_DELETE_FN;
  73. slapi_log_err(SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "--> statechange_init\n");
  74. if ((slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &plugin_entry) == 0) &&
  75. plugin_entry &&
  76. (plugin_type = slapi_entry_attr_get_charptr(plugin_entry, "nsslapd-plugintype")) &&
  77. plugin_type && strstr(plugin_type, "betxn")) {
  78. postadd = SLAPI_PLUGIN_BE_TXN_POST_ADD_FN;
  79. postmod = SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN;
  80. postmdn = SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN;
  81. postdel = SLAPI_PLUGIN_BE_TXN_POST_DELETE_FN;
  82. }
  83. slapi_ch_free_string(&plugin_type);
  84. head = 0;
  85. if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
  86. SLAPI_PLUGIN_VERSION_01) != 0 ||
  87. slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
  88. (void *)statechange_start) != 0 ||
  89. slapi_pblock_set(pb, postmod, (void *)statechange_mod_post_op) != 0 ||
  90. slapi_pblock_set(pb, postmdn, (void *)statechange_modrdn_post_op) != 0 ||
  91. slapi_pblock_set(pb, postadd, (void *)statechange_add_post_op) != 0 ||
  92. slapi_pblock_set(pb, postdel, (void *)statechange_delete_post_op) != 0 ||
  93. slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
  94. (void *)statechange_close) != 0 ||
  95. slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
  96. (void *)&pdesc) != 0) {
  97. slapi_log_err(SLAPI_LOG_ERR, SCN_PLUGIN_SUBSYSTEM,
  98. "statechange_init - Failed to register plugin\n");
  99. ret = SLAPI_PLUGIN_FAILURE;
  100. }
  101. slapi_log_err(SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_init\n");
  102. return ret;
  103. }
  104. /*
  105. statechange_start
  106. ---------
  107. This function publishes the interface for this plugin
  108. */
  109. static int
  110. statechange_start(Slapi_PBlock *pb __attribute__((unused)))
  111. {
  112. int ret = SLAPI_PLUGIN_SUCCESS;
  113. slapi_log_err(SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "--> statechange_start\n");
  114. api[0] = 0; /* reserved for api broker use, must be zero */
  115. api[1] = (void *)_statechange_register;
  116. api[2] = (void *)_statechange_unregister;
  117. api[3] = (void *)_statechange_unregister_all;
  118. api[4] = (void *)_statechange_vattr_cache_invalidator_callback;
  119. if (0 == (buffer_lock = slapi_new_mutex())) /* we never free this mutex */
  120. {
  121. /* badness */
  122. slapi_log_err(SLAPI_LOG_ERR, SCN_PLUGIN_SUBSYSTEM, "statechange_start - Failed to create lock\n");
  123. ret = SLAPI_PLUGIN_FAILURE;
  124. } else {
  125. if (slapi_apib_register(StateChange_v1_0_GUID, api)) {
  126. slapi_log_err(SLAPI_LOG_ERR, SCN_PLUGIN_SUBSYSTEM, "statechange_start - Failed to publish state change interface\n");
  127. ret = SLAPI_PLUGIN_FAILURE;
  128. }
  129. }
  130. head = 0;
  131. if (ret == SLAPI_PLUGIN_SUCCESS) {
  132. op_counter = slapi_counter_new();
  133. g_plugin_started = 1;
  134. }
  135. slapi_log_err(SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_start\n");
  136. return ret;
  137. }
  138. /*
  139. statechange_close
  140. ---------
  141. unregisters the interface for this plugin
  142. */
  143. static int
  144. statechange_close(Slapi_PBlock *pb __attribute__((unused)))
  145. {
  146. slapi_log_err(SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "--> statechange_close\n");
  147. g_plugin_started = 0;
  148. while (slapi_counter_get_value(op_counter) > 0) {
  149. PR_Sleep(PR_MillisecondsToInterval(100));
  150. }
  151. slapi_counter_destroy(&op_counter);
  152. slapi_apib_unregister(StateChange_v1_0_GUID);
  153. slapi_destroy_mutex(buffer_lock);
  154. buffer_lock = NULL;
  155. slapi_log_err(SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_close\n");
  156. return SLAPI_PLUGIN_SUCCESS;
  157. }
  158. static int
  159. statechange_mod_post_op(Slapi_PBlock *pb)
  160. {
  161. return statechange_post_op(pb, LDAP_CHANGETYPE_MODIFY);
  162. }
  163. static int
  164. statechange_modrdn_post_op(Slapi_PBlock *pb)
  165. {
  166. return statechange_post_op(pb, LDAP_CHANGETYPE_MODDN);
  167. }
  168. static int
  169. statechange_add_post_op(Slapi_PBlock *pb)
  170. {
  171. return statechange_post_op(pb, LDAP_CHANGETYPE_ADD);
  172. }
  173. static int
  174. statechange_delete_post_op(Slapi_PBlock *pb)
  175. {
  176. return statechange_post_op(pb, LDAP_CHANGETYPE_DELETE);
  177. }
  178. /*
  179. statechange_post_op
  180. -----------
  181. Catch all for all post operations that change entries
  182. in some way - evaluate the change against the notification
  183. entries and fire off the relevant callbacks - it is called
  184. from the real postop functions which supply it with the
  185. postop type
  186. */
  187. static int
  188. statechange_post_op(Slapi_PBlock *pb, int modtype)
  189. {
  190. SCNotify *notify = head;
  191. int execute;
  192. Slapi_DN *sdn = NULL;
  193. char *ndn = NULL;
  194. struct slapi_entry *e_before = NULL;
  195. struct slapi_entry *e_after = NULL;
  196. if (head == 0) {
  197. return SLAPI_PLUGIN_SUCCESS;
  198. }
  199. slapi_log_err(SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "--> statechange_post_op\n");
  200. /* evaluate this operation against the notification entries */
  201. slapi_lock_mutex(buffer_lock);
  202. if (head) {
  203. slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn);
  204. if (NULL == sdn) {
  205. slapi_log_err(SLAPI_LOG_ERR, SCN_PLUGIN_SUBSYSTEM,
  206. "statechange_post_op - Failed to get dn of changed entry");
  207. goto bail;
  208. }
  209. ndn = (char *)slapi_sdn_get_ndn(sdn);
  210. slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &e_before);
  211. slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e_after);
  212. do {
  213. execute = 0;
  214. /* first dn */
  215. if (notify->dn) {
  216. if (0 != slapi_dn_issuffix(ndn, notify->dn))
  217. execute = 1;
  218. } else
  219. /* note, if supplied null for everything in the entry *all* ops match */
  220. execute = 1;
  221. if (execute && notify->filter) {
  222. /* next the filter */
  223. int filter_test = 0;
  224. /* need to test entry both before and after op */
  225. if (e_before && !slapi_filter_test_simple(e_before, notify->realfilter))
  226. filter_test = 1;
  227. if (!filter_test && e_after && !slapi_filter_test_simple(e_after, notify->realfilter))
  228. filter_test = 1;
  229. if (!filter_test)
  230. execute = 0;
  231. }
  232. if (execute) {
  233. if (e_after)
  234. (notify->func)(e_after, ndn, modtype, pb, notify->caller_data);
  235. else
  236. (notify->func)(e_before, ndn, modtype, pb, notify->caller_data);
  237. }
  238. notify = notify->next;
  239. } while (notify && notify != head);
  240. }
  241. bail:
  242. slapi_unlock_mutex(buffer_lock);
  243. slapi_log_err(SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_post_op\n");
  244. return SLAPI_PLUGIN_SUCCESS; /* always succeed */
  245. }
  246. static int
  247. _statechange_register(char *caller_id, char *dn, char *filter, void *caller_data, notify_callback func)
  248. {
  249. int ret = SLAPI_PLUGIN_FAILURE;
  250. SCNotify *item;
  251. slapi_counter_increment(op_counter);
  252. if (!g_plugin_started) {
  253. slapi_counter_decrement(op_counter);
  254. return ret;
  255. }
  256. /* simple - we don't check for duplicates */
  257. item = (SCNotify *)slapi_ch_malloc(sizeof(SCNotify));
  258. if (item) {
  259. char *writable_filter = slapi_ch_strdup(filter);
  260. item->caller_id = slapi_ch_strdup(caller_id);
  261. if (dn) {
  262. item->dn = slapi_ch_strdup(dn);
  263. slapi_dn_normalize(item->dn);
  264. } else
  265. item->dn = 0;
  266. item->filter = slapi_ch_strdup(filter);
  267. item->caller_data = caller_data;
  268. if (writable_filter &&
  269. (NULL == (item->realfilter = slapi_str2filter(writable_filter)))) {
  270. slapi_log_err(SLAPI_LOG_ERR, SCN_PLUGIN_SUBSYSTEM,
  271. "_statechange_register - Invalid filter in statechange entry [%s]: [%s]\n",
  272. dn, filter);
  273. slapi_ch_free_string(&item->caller_id);
  274. slapi_ch_free_string(&item->dn);
  275. slapi_ch_free_string(&item->filter);
  276. slapi_ch_free_string(&writable_filter);
  277. slapi_ch_free((void **)&item);
  278. slapi_counter_decrement(op_counter);
  279. return ret;
  280. } else if (!writable_filter) {
  281. item->realfilter = NULL;
  282. }
  283. item->func = func;
  284. slapi_lock_mutex(buffer_lock);
  285. if (head == NULL) {
  286. head = item;
  287. head->next = head;
  288. head->prev = head;
  289. } else {
  290. item->next = head;
  291. item->prev = head->prev;
  292. head->prev = item;
  293. item->prev->next = item;
  294. }
  295. slapi_unlock_mutex(buffer_lock);
  296. slapi_ch_free_string(&writable_filter);
  297. ret = SLAPI_PLUGIN_SUCCESS;
  298. }
  299. slapi_counter_decrement(op_counter);
  300. return ret;
  301. }
  302. static void *
  303. _statechange_unregister(char *dn, char *filter, notify_callback thefunc)
  304. {
  305. void *ret = NULL;
  306. SCNotify *func = NULL;
  307. slapi_counter_increment(op_counter);
  308. if (!g_plugin_started || !buffer_lock) {
  309. slapi_counter_decrement(op_counter);
  310. return ret;
  311. }
  312. slapi_lock_mutex(buffer_lock);
  313. if ((func = statechange_find_notify(dn, filter, thefunc))) {
  314. func->prev->next = func->next;
  315. func->next->prev = func->prev;
  316. if (func == head) {
  317. head = func->next;
  318. }
  319. if (func == head) /* must be the last item, turn off the lights */
  320. head = 0;
  321. slapi_ch_free_string(&func->caller_id);
  322. slapi_ch_free_string(&func->dn);
  323. slapi_ch_free_string(&func->filter);
  324. slapi_filter_free(func->realfilter, 1);
  325. ret = func->caller_data;
  326. slapi_ch_free((void **)&func);
  327. }
  328. slapi_unlock_mutex(buffer_lock);
  329. slapi_counter_decrement(op_counter);
  330. return ret;
  331. }
  332. static void
  333. _statechange_unregister_all(char *caller_id, caller_data_free_callback callback)
  334. {
  335. SCNotify *notify = head;
  336. SCNotify *start_notify = head;
  337. slapi_counter_increment(op_counter);
  338. if (!g_plugin_started || !buffer_lock) {
  339. slapi_counter_decrement(op_counter);
  340. return;
  341. }
  342. slapi_lock_mutex(buffer_lock);
  343. if (notify) {
  344. do {
  345. SCNotify *notify_next = notify->next;
  346. if (slapi_utf8casecmp((unsigned char *)caller_id, (unsigned char *)notify->caller_id)) {
  347. notify->prev->next = notify->next;
  348. notify->next->prev = notify->prev;
  349. if (notify == head) {
  350. head = notify->next;
  351. start_notify = notify->prev;
  352. }
  353. if (notify == head) /* must be the last item, turn off the lights */
  354. head = 0;
  355. if (callback)
  356. callback(notify->caller_data);
  357. slapi_ch_free_string(&notify->caller_id);
  358. slapi_ch_free_string(&notify->dn);
  359. slapi_ch_free_string(&notify->filter);
  360. slapi_filter_free(notify->realfilter, 1);
  361. slapi_ch_free((void **)&notify);
  362. }
  363. notify = notify_next;
  364. } while (notify != start_notify && notify != NULL);
  365. }
  366. slapi_unlock_mutex(buffer_lock);
  367. slapi_counter_decrement(op_counter);
  368. }
  369. /* this func needs looking at to make work */
  370. static SCNotify *
  371. statechange_find_notify(char *dn, char *filter, notify_callback func)
  372. {
  373. SCNotify *notify = head;
  374. SCNotify *start_notify = head;
  375. if (notify) {
  376. do {
  377. if (!slapi_utf8casecmp((unsigned char *)dn, (unsigned char *)notify->dn) &&
  378. !slapi_utf8casecmp((unsigned char *)filter, (unsigned char *)notify->filter) && func == notify->func) {
  379. return notify;
  380. }
  381. notify = notify->next;
  382. } while (notify != start_notify);
  383. }
  384. return 0;
  385. }
  386. /* intended for use by vattr service providers
  387. * to deal with significant vattr state changes
  388. */
  389. static void
  390. _statechange_vattr_cache_invalidator_callback(Slapi_Entry *e,
  391. char *dn __attribute__((unused)),
  392. int modtype __attribute__((unused)),
  393. Slapi_PBlock *pb __attribute__((unused)),
  394. void *caller_data)
  395. {
  396. /* simply get the significance data and act */
  397. switch (*(int *)caller_data) {
  398. case STATECHANGE_VATTR_ENTRY_INVALIDATE:
  399. if (e)
  400. slapi_entry_vattrcache_watermark_invalidate(e);
  401. break;
  402. case STATECHANGE_VATTR_GLOBAL_INVALIDATE:
  403. default:
  404. slapi_entrycache_vattrcache_watermark_invalidate();
  405. break;
  406. }
  407. }