psearch.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  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. /*
  13. *
  14. * psearch.c - persistent search
  15. * August 1997, [email protected]
  16. *
  17. * Open issues:
  18. * - we increment and decrement active_threads in here. Are there
  19. * conditions under which this can prevent a server shutdown?
  20. */
  21. #include "slap.h"
  22. #include "fe.h"
  23. /*
  24. * A structure used to create a linked list
  25. * of entries being sent by a particular persistent
  26. * search result thread.
  27. * The ctrl is an "Entry Modify Notification" control
  28. * which we may send back with entries.
  29. */
  30. typedef struct _ps_entry_queue_node {
  31. Slapi_Entry *pe_entry;
  32. LDAPControl *pe_ctrls[2];
  33. struct _ps_entry_queue_node *pe_next;
  34. } PSEQNode;
  35. /*
  36. * Information about a single persistent search
  37. */
  38. typedef struct _psearch {
  39. Slapi_PBlock *ps_pblock;
  40. PRLock *ps_lock;
  41. PRInt32 ps_complete;
  42. PSEQNode *ps_eq_head;
  43. PSEQNode *ps_eq_tail;
  44. time_t ps_lasttime;
  45. ber_int_t ps_changetypes;
  46. int ps_send_entchg_controls;
  47. struct _psearch *ps_next;
  48. } PSearch;
  49. /*
  50. * A list of outstanding persistent searches.
  51. */
  52. typedef struct _psearch_list {
  53. Slapi_RWLock *pl_rwlock; /* R/W lock struct to serialize access */
  54. PSearch *pl_head; /* Head of list */
  55. PRLock *pl_cvarlock; /* Lock for cvar */
  56. PRCondVar *pl_cvar; /* ps threads sleep on this */
  57. } PSearch_List;
  58. /*
  59. * Convenience macros for locking the list of persistent searches
  60. */
  61. #define PSL_LOCK_READ() slapi_rwlock_rdlock(psearch_list->pl_rwlock)
  62. #define PSL_UNLOCK_READ() slapi_rwlock_unlock(psearch_list->pl_rwlock)
  63. #define PSL_LOCK_WRITE() slapi_rwlock_wrlock(psearch_list->pl_rwlock)
  64. #define PSL_UNLOCK_WRITE() slapi_rwlock_unlock(psearch_list->pl_rwlock)
  65. /*
  66. * Convenience macro for checking if the Persistent Search subsystem has
  67. * been initialized.
  68. */
  69. #define PS_IS_INITIALIZED() (psearch_list != NULL)
  70. /* Main list of outstanding persistent searches */
  71. static PSearch_List *psearch_list = NULL;
  72. /* Forward declarations */
  73. static void ps_send_results( void *arg );
  74. static PSearch *psearch_alloc();
  75. static void ps_add_ps( PSearch *ps );
  76. static void ps_remove( PSearch *dps );
  77. static void pe_ch_free( PSEQNode **pe );
  78. static int create_entrychange_control( ber_int_t chgtype, ber_int_t chgnum,
  79. const char *prevdn, LDAPControl **ctrlp );
  80. /*
  81. * Initialize the list structure which contains the list
  82. * of outstanding persistent searches. This must be
  83. * called early during server startup.
  84. */
  85. void
  86. ps_init_psearch_system()
  87. {
  88. if ( !PS_IS_INITIALIZED()) {
  89. psearch_list = (PSearch_List *) slapi_ch_calloc( 1, sizeof( PSearch_List ));
  90. if (( psearch_list->pl_rwlock = slapi_new_rwlock()) == NULL ) {
  91. LDAPDebug( LDAP_DEBUG_ANY, "init_psearch_list: cannot initialize lock structure. "
  92. "The server is terminating.\n", 0, 0, 0 );
  93. exit( -1 );
  94. }
  95. if (( psearch_list->pl_cvarlock = PR_NewLock()) == NULL ) {
  96. LDAPDebug( LDAP_DEBUG_ANY, "init_psearch_list: cannot create new lock. "
  97. "The server is terminating.\n", 0, 0, 0 );
  98. exit( -1 );
  99. }
  100. if (( psearch_list->pl_cvar = PR_NewCondVar( psearch_list->pl_cvarlock )) == NULL ) {
  101. LDAPDebug( LDAP_DEBUG_ANY, "init_psearch_list: cannot create new condition variable. "
  102. "The server is terminating.\n", 0, 0, 0 );
  103. exit( -1 );
  104. }
  105. psearch_list->pl_head = NULL;
  106. }
  107. }
  108. /*
  109. * Close all outstanding persistent searches.
  110. * To be used when the server is shutting down.
  111. */
  112. void
  113. ps_stop_psearch_system()
  114. {
  115. PSearch *ps;
  116. if ( PS_IS_INITIALIZED()) {
  117. PSL_LOCK_WRITE();
  118. for ( ps = psearch_list->pl_head; NULL != ps; ps = ps->ps_next ) {
  119. PR_AtomicIncrement( &ps->ps_complete );
  120. }
  121. PSL_UNLOCK_WRITE();
  122. ps_wakeup_all();
  123. }
  124. }
  125. static Slapi_PBlock *
  126. pblock_copy(Slapi_PBlock *src)
  127. {
  128. Slapi_PBlock *dest = slapi_pblock_new();
  129. *dest = *src;
  130. return dest;
  131. }
  132. /*
  133. * Add the given pblock to the list of outstanding persistent searches.
  134. * Then, start a thread to send the results to the client as they
  135. * are dispatched by add, modify, and modrdn operations.
  136. */
  137. void
  138. ps_add( Slapi_PBlock *pb, ber_int_t changetypes, int send_entchg_controls )
  139. {
  140. PSearch *ps;
  141. PRThread *ps_tid;
  142. if ( PS_IS_INITIALIZED() && NULL != pb ) {
  143. /* Create the new node */
  144. ps = psearch_alloc();
  145. ps->ps_pblock = pblock_copy(pb);
  146. ps->ps_changetypes = changetypes;
  147. ps->ps_send_entchg_controls = send_entchg_controls;
  148. /* Add it to the head of the list of persistent searches */
  149. ps_add_ps( ps );
  150. /* Start a thread to send the results */
  151. ps_tid = PR_CreateThread( PR_USER_THREAD, ps_send_results,
  152. (void *) ps, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  153. PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE );
  154. /* Checking if the thread is succesfully created and
  155. * if the thread is not created succesfully.... we send
  156. * error messages to the Log file
  157. */
  158. if(NULL == ps_tid){
  159. int prerr;
  160. prerr = PR_GetError();
  161. LDAPDebug(LDAP_DEBUG_ANY,"persistent search PR_CreateThread()failed in the "
  162. "ps_add function: " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
  163. prerr, slapd_pr_strerror(prerr), 0);
  164. /* Now remove the ps from the list so call the function ps_remove */
  165. ps_remove(ps);
  166. PR_DestroyLock ( ps->ps_lock );
  167. ps->ps_lock = NULL;
  168. slapi_ch_free((void **) &ps->ps_pblock );
  169. slapi_ch_free((void **) &ps );
  170. }
  171. }
  172. }
  173. /*
  174. * Remove the given PSearch from the list of outstanding persistent
  175. * searches and delete its resources.
  176. */
  177. static void
  178. ps_remove( PSearch *dps )
  179. {
  180. PSearch *ps;
  181. if ( PS_IS_INITIALIZED() && NULL != dps ) {
  182. PSL_LOCK_WRITE();
  183. if ( dps == psearch_list->pl_head ) {
  184. /* Remove from head */
  185. psearch_list->pl_head = psearch_list->pl_head->ps_next;
  186. } else {
  187. /* Find and remove from list */
  188. ps = psearch_list->pl_head;
  189. while ( NULL != ps->ps_next ) {
  190. if ( ps->ps_next == dps ) {
  191. ps->ps_next = ps->ps_next->ps_next;
  192. break;
  193. } else {
  194. ps = ps->ps_next;
  195. }
  196. }
  197. }
  198. PSL_UNLOCK_WRITE();
  199. }
  200. }
  201. /*
  202. * Free a persistent search node (and everything it holds).
  203. */
  204. static void
  205. pe_ch_free( PSEQNode **pe )
  206. {
  207. if ( pe != NULL && *pe != NULL ) {
  208. if ( (*pe)->pe_entry != NULL ) {
  209. slapi_entry_free( (*pe)->pe_entry );
  210. (*pe)->pe_entry = NULL;
  211. }
  212. if ( (*pe)->pe_ctrls[0] != NULL ) {
  213. ldap_control_free( (*pe)->pe_ctrls[0] );
  214. (*pe)->pe_ctrls[0] = NULL;
  215. }
  216. slapi_ch_free( (void **)pe );
  217. }
  218. }
  219. /*
  220. * Thread routine for sending search results to a client
  221. * which is persistently waiting for them.
  222. *
  223. * This routine will terminate when either (a) the ps_complete
  224. * flag is set, or (b) the associated operation is abandoned.
  225. * In any case, the thread won't notice until it wakes from
  226. * sleeping on the ps_list condition variable, so it needs
  227. * to be awakened.
  228. */
  229. static void
  230. ps_send_results( void *arg )
  231. {
  232. PSearch *ps = (PSearch *)arg;
  233. PSEQNode *peq, *peqnext;
  234. struct slapi_filter *filter = 0;
  235. char *base = NULL;
  236. Slapi_DN *sdn = NULL;
  237. char *fstr = NULL;
  238. char **pbattrs = NULL;
  239. int conn_acq_flag = 0;
  240. Slapi_Connection *conn = NULL;
  241. g_incr_active_threadcnt();
  242. /* need to acquire a reference to this connection so that it will not
  243. be released or cleaned up out from under us */
  244. PR_EnterMonitor(ps->ps_pblock->pb_conn->c_mutex);
  245. conn_acq_flag = connection_acquire_nolock(ps->ps_pblock->pb_conn);
  246. PR_ExitMonitor(ps->ps_pblock->pb_conn->c_mutex);
  247. if (conn_acq_flag) {
  248. slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search",
  249. "conn=%" NSPRIu64 " op=%d Could not acquire the connection - psearch aborted\n",
  250. ps->ps_pblock->pb_conn->c_connid, ps->ps_pblock->pb_op->o_opid);
  251. }
  252. PR_Lock( psearch_list->pl_cvarlock );
  253. while ( (conn_acq_flag == 0) && !ps->ps_complete ) {
  254. /* Check for an abandoned operation */
  255. if ( ps->ps_pblock->pb_op == NULL || slapi_op_abandoned( ps->ps_pblock ) ) {
  256. slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search",
  257. "conn=%" NSPRIu64 " op=%d The operation has been abandoned\n",
  258. ps->ps_pblock->pb_conn->c_connid, ps->ps_pblock->pb_op->o_opid);
  259. break;
  260. }
  261. if ( NULL == ps->ps_eq_head ) {
  262. /* Nothing to do */
  263. PR_WaitCondVar( psearch_list->pl_cvar, PR_INTERVAL_NO_TIMEOUT );
  264. } else {
  265. /* dequeue the item */
  266. int attrsonly;
  267. char **attrs;
  268. LDAPControl **ectrls;
  269. Slapi_Entry *ec;
  270. Slapi_Filter *f = NULL;
  271. PR_Lock( ps->ps_lock );
  272. peq = ps->ps_eq_head;
  273. ps->ps_eq_head = peq->pe_next;
  274. if ( NULL == ps->ps_eq_head ) {
  275. ps->ps_eq_tail = NULL;
  276. }
  277. PR_Unlock( ps->ps_lock );
  278. /* Get all the information we need to send the result */
  279. ec = peq->pe_entry;
  280. slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_ATTRS, &attrs );
  281. slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_ATTRSONLY, &attrsonly );
  282. if ( !ps->ps_send_entchg_controls || peq->pe_ctrls[0] == NULL ) {
  283. ectrls = NULL;
  284. } else {
  285. ectrls = peq->pe_ctrls;
  286. }
  287. /*
  288. * Send the result. Since send_ldap_search_entry can block for
  289. * up to 30 minutes, we relinquish all locks before calling it.
  290. */
  291. PR_Unlock(psearch_list->pl_cvarlock);
  292. /*
  293. * The entry is in the right scope and matches the filter
  294. * but we need to redo the filter test here to check access
  295. * controls. See the comments at the slapi_filter_test()
  296. * call in ps_service_persistent_searches().
  297. */
  298. slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_FILTER, &f );
  299. /* See if the entry meets the filter and ACL criteria */
  300. if ( slapi_vattr_filter_test( ps->ps_pblock, ec, f,
  301. 1 /* verify_access */ ) == 0 ) {
  302. int rc = 0;
  303. slapi_pblock_set( ps->ps_pblock, SLAPI_SEARCH_RESULT_ENTRY, ec );
  304. rc = send_ldap_search_entry( ps->ps_pblock, ec,
  305. ectrls, attrs, attrsonly );
  306. if (rc) {
  307. slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search",
  308. "conn=%" NSPRIu64 " op=%d Error %d sending entry %s with op status %d\n",
  309. ps->ps_pblock->pb_conn->c_connid, ps->ps_pblock->pb_op->o_opid,
  310. rc, slapi_entry_get_dn_const(ec), ps->ps_pblock->pb_op->o_status);
  311. }
  312. }
  313. PR_Lock(psearch_list->pl_cvarlock);
  314. /* Deallocate our wrapper for this entry */
  315. pe_ch_free( &peq );
  316. }
  317. }
  318. PR_Unlock( psearch_list->pl_cvarlock );
  319. ps_remove( ps );
  320. /* indicate the end of search */
  321. plugin_call_plugins( ps->ps_pblock , SLAPI_PLUGIN_POST_SEARCH_FN );
  322. /* free things from the pblock that were not free'd in do_search() */
  323. /* we strdup'd this in search.c - need to free */
  324. slapi_pblock_get( ps->ps_pblock, SLAPI_ORIGINAL_TARGET_DN, &base );
  325. slapi_pblock_set( ps->ps_pblock, SLAPI_ORIGINAL_TARGET_DN, NULL );
  326. slapi_ch_free_string(&base);
  327. /* Free SLAPI_SEARCH_* before deleting op since those are held by op */
  328. slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_TARGET_SDN, &sdn );
  329. slapi_pblock_set( ps->ps_pblock, SLAPI_SEARCH_TARGET_SDN, NULL );
  330. slapi_sdn_free(&sdn);
  331. slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_STRFILTER, &fstr );
  332. slapi_pblock_set( ps->ps_pblock, SLAPI_SEARCH_STRFILTER, NULL );
  333. slapi_ch_free_string(&fstr);
  334. slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_ATTRS, &pbattrs );
  335. slapi_pblock_set( ps->ps_pblock, SLAPI_SEARCH_ATTRS, NULL );
  336. if ( pbattrs != NULL )
  337. {
  338. charray_free( pbattrs );
  339. }
  340. slapi_pblock_get(ps->ps_pblock, SLAPI_SEARCH_FILTER, &filter );
  341. slapi_pblock_set(ps->ps_pblock, SLAPI_SEARCH_FILTER, NULL );
  342. slapi_filter_free(filter, 1);
  343. conn = ps->ps_pblock->pb_conn; /* save to release later - connection_remove_operation_ext will NULL the pb_conn */
  344. /* Clean up the connection structure */
  345. PR_EnterMonitor(conn->c_mutex);
  346. slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search",
  347. "conn=%" NSPRIu64 " op=%d Releasing the connection and operation\n",
  348. conn->c_connid, ps->ps_pblock->pb_op->o_opid);
  349. /* Delete this op from the connection's list */
  350. connection_remove_operation_ext( ps->ps_pblock, conn, ps->ps_pblock->pb_op );
  351. /* Decrement the connection refcnt */
  352. if (conn_acq_flag == 0) { /* we acquired it, so release it */
  353. connection_release_nolock (conn);
  354. }
  355. PR_ExitMonitor(conn->c_mutex);
  356. conn = NULL;
  357. PR_DestroyLock ( ps->ps_lock );
  358. ps->ps_lock = NULL;
  359. slapi_ch_free((void **) &ps->ps_pblock );
  360. for ( peq = ps->ps_eq_head; peq; peq = peqnext) {
  361. peqnext = peq->pe_next;
  362. pe_ch_free( &peq );
  363. }
  364. slapi_ch_free((void **) &ps );
  365. g_decr_active_threadcnt();
  366. }
  367. /*
  368. * Allocate and initialize an empty PSearch node.
  369. */
  370. static PSearch *
  371. psearch_alloc()
  372. {
  373. PSearch *ps;
  374. ps = (PSearch *) slapi_ch_calloc( 1, sizeof( PSearch ));
  375. ps->ps_pblock = NULL;
  376. if (( ps->ps_lock = PR_NewLock()) == NULL ) {
  377. LDAPDebug( LDAP_DEBUG_ANY, "psearch_add: cannot create new lock. "
  378. "Persistent search abandoned.\n", 0, 0, 0 );
  379. slapi_ch_free((void **)&ps);
  380. return( NULL );
  381. }
  382. ps->ps_complete = 0;
  383. ps->ps_eq_head = ps->ps_eq_tail = (PSEQNode *) NULL;
  384. ps->ps_lasttime = (time_t) 0L;
  385. ps->ps_next = NULL;
  386. return ps;
  387. }
  388. /*
  389. * Add the given persistent search to the
  390. * head of the list of persistent searches.
  391. */
  392. static void
  393. ps_add_ps( PSearch *ps )
  394. {
  395. if ( PS_IS_INITIALIZED() && NULL != ps ) {
  396. PSL_LOCK_WRITE();
  397. ps->ps_next = psearch_list->pl_head;
  398. psearch_list->pl_head = ps;
  399. PSL_UNLOCK_WRITE();
  400. }
  401. }
  402. /*
  403. * Wake up all threads sleeping on
  404. * the psearch_list condition variable.
  405. */
  406. void
  407. ps_wakeup_all()
  408. {
  409. if ( PS_IS_INITIALIZED()) {
  410. PR_Lock( psearch_list->pl_cvarlock );
  411. PR_NotifyAllCondVar( psearch_list->pl_cvar );
  412. PR_Unlock( psearch_list->pl_cvarlock );
  413. }
  414. }
  415. /*
  416. * Check if there are any persistent searches. If so,
  417. * the check to see if the chgtype is one of those the
  418. * client is interested in. If so, then check to see if
  419. * the entry matches any of the filters the searches.
  420. * If so, then enqueue the entry on that persistent search's
  421. * ps_entryqueue and signal it to wake up and send the entry.
  422. *
  423. * Note that if eprev is NULL we assume that the entry's DN
  424. * was not changed by the op. that called this function. If
  425. * chgnum is 0 it is unknown so we won't ever send it to a
  426. * client in the EntryChangeNotification control.
  427. */
  428. void
  429. ps_service_persistent_searches( Slapi_Entry *e, Slapi_Entry *eprev, ber_int_t chgtype,
  430. ber_int_t chgnum )
  431. {
  432. LDAPControl *ctrl = NULL;
  433. PSearch *ps = NULL;
  434. PSEQNode *pe = NULL;
  435. int matched = 0;
  436. const char *edn;
  437. if ( !PS_IS_INITIALIZED()) {
  438. return;
  439. }
  440. if ( NULL == e ) {
  441. /* For now, some backends such as the chaining backend do not provide a post-op entry */
  442. return;
  443. }
  444. PSL_LOCK_READ();
  445. edn = slapi_entry_get_dn_const(e);
  446. for ( ps = psearch_list ? psearch_list->pl_head : NULL; NULL != ps; ps = ps->ps_next ) {
  447. char *origbase = NULL;
  448. Slapi_DN *base = NULL;
  449. Slapi_Filter *f;
  450. int scope;
  451. /* Skip the node that doesn't meet the changetype,
  452. * or is unable to use the change in ps_send_results()
  453. */
  454. if (( ps->ps_changetypes & chgtype ) == 0 ||
  455. ps->ps_pblock->pb_op == NULL ||
  456. slapi_op_abandoned( ps->ps_pblock ) ) {
  457. continue;
  458. }
  459. slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search",
  460. "conn=%" NSPRIu64 " op=%d entry %s with chgtype %d "
  461. "matches the ps changetype %d\n",
  462. ps->ps_pblock->pb_conn->c_connid,
  463. ps->ps_pblock->pb_op->o_opid,
  464. edn, chgtype, ps->ps_changetypes);
  465. slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_FILTER, &f );
  466. slapi_pblock_get( ps->ps_pblock, SLAPI_ORIGINAL_TARGET_DN, &origbase );
  467. slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_TARGET_SDN, &base );
  468. slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_SCOPE, &scope );
  469. if (NULL == base) {
  470. base = slapi_sdn_new_dn_byref(origbase);
  471. slapi_pblock_set(ps->ps_pblock, SLAPI_SEARCH_TARGET_SDN, base);
  472. }
  473. /*
  474. * See if the entry meets the scope and filter criteria.
  475. * We cannot do the acl check here as this thread
  476. * would then potentially clash with the ps_send_results()
  477. * thread on the aclpb in ps->ps_pblock.
  478. * By avoiding the acl check in this thread, and leaving all the acl
  479. * checking to the ps_send_results() thread we avoid
  480. * the ps_pblock contention problem.
  481. * The lesson here is "Do not give multiple threads arbitary access
  482. * to the same pblock" this kind of muti-threaded access
  483. * to the same pblock must be done carefully--there is currently no
  484. * generic satisfactory way to do this.
  485. */
  486. if ( slapi_sdn_scope_test( slapi_entry_get_sdn_const(e), base, scope ) &&
  487. slapi_vattr_filter_test( ps->ps_pblock, e, f, 0 /* verify_access */ ) == 0 ) {
  488. PSEQNode *pOldtail;
  489. /* The scope and the filter match - enqueue it */
  490. matched++;
  491. pe = (PSEQNode *)slapi_ch_calloc( 1, sizeof( PSEQNode ));
  492. pe->pe_entry = slapi_entry_dup( e );
  493. if ( ps->ps_send_entchg_controls ) {
  494. /* create_entrychange_control() is more
  495. * expensive than slapi_dup_control()
  496. */
  497. if ( ctrl == NULL ) {
  498. int rc;
  499. rc = create_entrychange_control( chgtype, chgnum,
  500. eprev ? slapi_entry_get_dn_const(eprev) : NULL,
  501. &ctrl );
  502. if ( rc != LDAP_SUCCESS ) {
  503. LDAPDebug( LDAP_DEBUG_ANY, "ps_service_persistent_searches:"
  504. " unable to create EntryChangeNotification control for"
  505. " entry \"%s\" -- control won't be sent.\n",
  506. slapi_entry_get_dn_const(e), 0, 0 );
  507. }
  508. }
  509. if ( ctrl ) {
  510. pe->pe_ctrls[0] = slapi_dup_control( ctrl );
  511. }
  512. }
  513. /* Put it on the end of the list for this pers search */
  514. PR_Lock( ps->ps_lock );
  515. pOldtail = ps->ps_eq_tail;
  516. ps->ps_eq_tail = pe;
  517. if ( NULL == ps->ps_eq_head ) {
  518. ps->ps_eq_head = ps->ps_eq_tail;
  519. }
  520. else {
  521. pOldtail->pe_next = ps->ps_eq_tail;
  522. }
  523. PR_Unlock( ps->ps_lock );
  524. }
  525. }
  526. PSL_UNLOCK_READ();
  527. /* Were there any matches? */
  528. if ( matched ) {
  529. ldap_control_free( ctrl );
  530. /* Turn 'em loose */
  531. ps_wakeup_all();
  532. LDAPDebug( LDAP_DEBUG_TRACE, "ps_service_persistent_searches: enqueued entry "
  533. "\"%s\" on %d persistent search lists\n", slapi_entry_get_dn_const(e), matched, 0 );
  534. } else {
  535. LDAPDebug( LDAP_DEBUG_TRACE, "ps_service_persistent_searches: entry "
  536. "\"%s\" not enqueued on any persistent search lists\n", slapi_entry_get_dn_const(e), 0, 0 );
  537. }
  538. }
  539. /*
  540. * Parse the value from an LDAPv3 "Persistent Search" control. They look
  541. * like this:
  542. *
  543. * PersistentSearch ::= SEQUENCE {
  544. * changeTypes INTEGER,
  545. * -- the changeTypes field is the logical OR of
  546. * -- one or more of these values: add (1), delete (2),
  547. * -- modify (4), modDN (8). It specifies which types of
  548. * -- changes will cause an entry to be returned.
  549. * changesOnly BOOLEAN, -- skip initial search?
  550. * returnECs BOOLEAN, -- return "Entry Change" controls?
  551. * }
  552. *
  553. * Return an LDAP error code (LDAP_SUCCESS if all goes well).
  554. *
  555. * This function is standalone; it does not require initialization of
  556. * the PS subsystem.
  557. */
  558. int
  559. ps_parse_control_value( struct berval *psbvp, ber_int_t *changetypesp, int *changesonlyp, int *returnecsp )
  560. {
  561. int rc= LDAP_SUCCESS;
  562. if ( psbvp->bv_len == 0 || psbvp->bv_val == NULL )
  563. {
  564. rc= LDAP_PROTOCOL_ERROR;
  565. }
  566. else
  567. {
  568. BerElement *ber= ber_init( psbvp );
  569. if ( ber == NULL )
  570. {
  571. rc= LDAP_OPERATIONS_ERROR;
  572. }
  573. else
  574. {
  575. if ( ber_scanf( ber, "{ibb}", changetypesp, changesonlyp, returnecsp ) == LBER_ERROR )
  576. {
  577. rc= LDAP_PROTOCOL_ERROR;
  578. }
  579. /* the ber encoding is no longer needed */
  580. ber_free(ber,1);
  581. }
  582. }
  583. return( rc );
  584. }
  585. /*
  586. * Create an LDAPv3 "Entry Change Notification" control. They look like this:
  587. *
  588. * EntryChangeNotification ::= SEQUENCE {
  589. * changeType ENUMERATED {
  590. * add (1), -- LDAP_CHANGETYPE_ADD
  591. * delete (2), -- LDAP_CHANGETYPE_DELETE
  592. * modify (4), -- LDAP_CHANGETYPE_MODIFY
  593. * moddn (8), -- LDAP_CHANGETYPE_MODDN
  594. * },
  595. * previousDN LDAPDN OPTIONAL, -- included for MODDN ops. only
  596. * changeNumber INTEGER OPTIONAL, -- included if supported by DSA
  597. * }
  598. *
  599. * This function returns an LDAP error code (LDAP_SUCCESS if all goes well).
  600. * The value returned in *ctrlp should be free'd use ldap_control_free().
  601. * If chgnum is 0 we omit it from the control.
  602. */
  603. static int
  604. create_entrychange_control( ber_int_t chgtype, ber_int_t chgnum, const char *dn,
  605. LDAPControl **ctrlp )
  606. {
  607. int rc;
  608. BerElement *ber;
  609. struct berval *bvp;
  610. const char *prevdn= dn;
  611. if ( prevdn == NULL ) {
  612. prevdn = "";
  613. }
  614. if ( ctrlp == NULL || ( ber = der_alloc()) == NULL ) {
  615. return( LDAP_OPERATIONS_ERROR );
  616. }
  617. *ctrlp = NULL;
  618. if (( rc = ber_printf( ber, "{e", chgtype )) != -1 ) {
  619. if ( chgtype == LDAP_CHANGETYPE_MODDN ) {
  620. rc = ber_printf( ber, "s", prevdn );
  621. }
  622. if ( rc != -1 && chgnum != 0 ) {
  623. rc = ber_printf( ber, "i", chgnum );
  624. }
  625. if ( rc != -1 ) {
  626. rc = ber_printf( ber, "}" );
  627. }
  628. }
  629. if ( rc != -1 ) {
  630. rc = ber_flatten( ber, &bvp );
  631. }
  632. ber_free( ber, 1 );
  633. if ( rc == -1 ) {
  634. return( LDAP_OPERATIONS_ERROR );
  635. }
  636. *ctrlp = (LDAPControl *)slapi_ch_malloc( sizeof( LDAPControl ));
  637. (*ctrlp)->ldctl_iscritical = 0;
  638. (*ctrlp)->ldctl_oid = slapi_ch_strdup( LDAP_CONTROL_ENTRYCHANGE );
  639. (*ctrlp)->ldctl_value = *bvp; /* struct copy */
  640. bvp->bv_val = NULL;
  641. ber_bvfree( bvp );
  642. return( LDAP_SUCCESS );
  643. }