psearch.c 23 KB

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