psearch.c 23 KB

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