cb_conn_stateless.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960
  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. #include "cb.h"
  42. /*
  43. * Most of the complicated connection-related code lives in this file. Some
  44. * general notes about how we manage our connections to "remote" LDAP servers:
  45. *
  46. * 1) Each farm server we have a relationship with is managed independently.
  47. *
  48. * 2) We may simultaneously issue multiple requests on a single LDAP
  49. * connection. Each server has a "maxconcurrency" configuration
  50. * parameter associated with it that caps the number of outstanding operations
  51. * per connection. For each connection we maintain a "usecount"
  52. * which is used to track the number of threads using the connection.
  53. *
  54. * 3) IMPORTANT NOTE: This connexion management is stateless i.e there is no garanty that
  55. * operation from the same incoming client connections are sent to the same
  56. * outgoing connection to the farm server. Today, this is not a problem because
  57. * all controls we support are stateless. The implementation of the abandon
  58. * operation takes this limitation into account.
  59. *
  60. * 4) We may open more than one connection to a server. Each farm server
  61. * has a "maxconnections" configuration parameter associated with it
  62. * that caps the number of connections.
  63. *
  64. * 5) If no connection is available to service a request , threads
  65. * go to sleep on a condition variable and one is woken up each time
  66. * a connection's "usecount" is decremented.
  67. *
  68. * 6) If we see an LDAP_CONNECT_ERROR or LDAP_SERVER_DOWN error on a
  69. * session handle, we mark its status as CB_LDAP_STATUS_DOWN and
  70. * close it as soon as all threads using it release it. Connections
  71. * marked as "down" are not counted against the "maxconnections" limit.
  72. *
  73. * 7) We close and reopen connections that have been open for more than
  74. * the server's configured connection lifetime. This is done to ensure
  75. * that we reconnect to a primary server after failover occurs. If no
  76. * lifetime is configured or it is set to 0, we never close and reopen
  77. * connections.
  78. */
  79. static void cb_close_and_dispose_connection ( cb_outgoing_conn * conn );
  80. static void cb_check_for_stale_connections(cb_conn_pool * pool);
  81. PRUint32 PR_GetThreadID(PRThread *thread);
  82. /* returns the threadId of the current thread modulo MAX_CONN_ARRAY
  83. => gives the position of the thread in the array of secure connections */
  84. static int PR_ThreadSelf() {
  85. PRThread *thr = PR_GetCurrentThread();
  86. PRUint32 myself = PR_GetThreadID(thr);
  87. myself &= 0x000007FF ;
  88. return myself;
  89. }
  90. static int PR_MyThreadId() {
  91. PRThread *thr = PR_GetCurrentThread();
  92. PRUint32 myself = PR_GetThreadID(thr);
  93. return myself;
  94. }
  95. /*
  96. ** Close outgoing connections
  97. */
  98. void cb_close_conn_pool(cb_conn_pool * pool) {
  99. cb_outgoing_conn *conn, *nextconn;
  100. int secure = pool->secure;
  101. int i = 0;
  102. slapi_lock_mutex( pool->conn.conn_list_mutex );
  103. if (secure) {
  104. for (i=0; i< MAX_CONN_ARRAY; i++) {
  105. for (conn = pool->connarray[i]; conn != NULL; conn = nextconn) {
  106. if ( conn->status != CB_CONNSTATUS_OK ) {
  107. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  108. "cb_close_conn_pool: unexpected connection state (%d)\n",conn->status);
  109. }
  110. nextconn=conn->next;
  111. cb_close_and_dispose_connection(conn);
  112. }
  113. }
  114. }
  115. else {
  116. for ( conn = pool->conn.conn_list; conn != NULL; conn = nextconn ) {
  117. if ( conn->status != CB_CONNSTATUS_OK ) {
  118. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  119. "cb_close_conn_pool: unexpected connection state (%d)\n",conn->status);
  120. }
  121. nextconn=conn->next;
  122. cb_close_and_dispose_connection(conn);
  123. }
  124. }
  125. pool->conn.conn_list=NULL;
  126. pool->conn.conn_list_count=0;
  127. slapi_unlock_mutex( pool->conn.conn_list_mutex );
  128. }
  129. /*
  130. * Get an LDAP session handle for communicating with the farm servers.
  131. *
  132. * Returns an LDAP eror code, typically:
  133. * LDAP_SUCCESS
  134. * LDAP_TIMELIMIT_EXCEEDED
  135. * LDAP_CONNECT_ERROR
  136. * NOTE : if maxtime NULL, use operation timeout
  137. */
  138. int cb_get_connection(cb_conn_pool * pool, LDAP ** lld, cb_outgoing_conn ** cc,struct timeval * maxtime, char **errmsg) {
  139. int rc=LDAP_SUCCESS; /* optimistic */
  140. cb_outgoing_conn *conn=NULL;
  141. cb_outgoing_conn *connprev=NULL;
  142. LDAP *ld=NULL;
  143. time_t endbefore=0;
  144. int checktime=0;
  145. struct timeval bind_to, op_to;
  146. unsigned int maxconcurrency,maxconnections;
  147. char *password,*binddn,*hostname;
  148. unsigned int port;
  149. int secure;
  150. char *mech = NULL;;
  151. static char *error1="Can't contact remote server : %s";
  152. static char *error2="Can't bind to remote server : %s";
  153. int isMultiThread = ENABLE_MULTITHREAD_PER_CONN ; /* by default, we enable multiple operations per connection */
  154. /*
  155. ** return an error if we can't get a connection
  156. ** before the operation timeout has expired
  157. ** bind_timeout: timeout for the bind operation (if bind needed)
  158. ** ( checked in ldap_result )
  159. ** op_timeout: timeout for the op that needs a connection
  160. ** ( checked in the loop )
  161. */
  162. *cc=NULL;
  163. PR_RWLock_Rlock(pool->rwl_config_lock);
  164. maxconcurrency=pool->conn.maxconcurrency;
  165. maxconnections=pool->conn.maxconnections;
  166. bind_to.tv_sec = pool->conn.bind_timeout.tv_sec;
  167. bind_to.tv_usec = pool->conn.bind_timeout.tv_usec;
  168. op_to.tv_sec = pool->conn.op_timeout.tv_sec;
  169. op_to.tv_usec = pool->conn.op_timeout.tv_usec;
  170. /* SD 02/10/2000 temp fix */
  171. /* allow dynamic update of the binddn & password */
  172. /* host, port and security mode */
  173. /* previous values are NOT freed when changed */
  174. /* won't likely to be changed often */
  175. /* pointers put in the waste basket fields and */
  176. /* freed when the backend is stopped. */
  177. password=pool->password;
  178. binddn=pool->binddn;
  179. hostname=pool->hostname;
  180. port=pool->port;
  181. secure=pool->secure;
  182. if (pool->starttls) {
  183. secure = 2;
  184. }
  185. mech=pool->mech;
  186. PR_RWLock_Unlock(pool->rwl_config_lock);
  187. if (secure) {
  188. isMultiThread = DISABLE_MULTITHREAD_PER_CONN ;
  189. }
  190. /* For stupid admins */
  191. if (maxconnections <=0) {
  192. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  193. "<== cb_get_connection error (no connection available)\n");
  194. if ( errmsg ) {
  195. *errmsg = PR_smprintf(error1, "no connection available");
  196. }
  197. return LDAP_CONNECT_ERROR;
  198. }
  199. if (maxtime) {
  200. if (maxtime->tv_sec != 0) {
  201. checktime=1;
  202. endbefore = current_time() + maxtime->tv_sec;
  203. /* make sure bind to <= operation timeout */
  204. if ((bind_to.tv_sec==0) || (bind_to.tv_sec > maxtime->tv_sec))
  205. bind_to.tv_sec=maxtime->tv_sec;
  206. }
  207. } else {
  208. if (op_to.tv_sec != 0) {
  209. checktime=1;
  210. endbefore = current_time() + op_to.tv_sec;
  211. /* make sure bind to <= operation timeout */
  212. if ((bind_to.tv_sec==0) || (bind_to.tv_sec > op_to.tv_sec))
  213. bind_to.tv_sec=op_to.tv_sec;
  214. }
  215. }
  216. /*
  217. * Close (or mark to be closed) any connections for this farm server that have
  218. * exceeded the maximum connection lifetime.
  219. */
  220. cb_check_for_stale_connections(pool);
  221. /*
  222. * Look for an available, already open connection
  223. */
  224. slapi_lock_mutex( pool->conn.conn_list_mutex );
  225. if (cb_debug_on()) {
  226. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  227. "==> cb_get_connection server %s conns: %d maxconns: %d\n",
  228. hostname, pool->conn.conn_list_count, maxconnections );
  229. }
  230. for (;;) {
  231. /* time limit mgmt */
  232. if (checktime) {
  233. if (current_time() > endbefore ) {
  234. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  235. "cb_get_connection server %s expired.\n", hostname );
  236. if ( errmsg ) {
  237. *errmsg = PR_smprintf(error1,"timelimit exceeded");
  238. }
  239. rc=LDAP_TIMELIMIT_EXCEEDED;
  240. conn=NULL;
  241. ld=NULL;
  242. goto unlock_and_return;
  243. }
  244. }
  245. /*
  246. * First, look for an available, already open/bound connection
  247. */
  248. if (secure) {
  249. for (conn = pool->connarray[PR_ThreadSelf()]; conn != NULL; conn = conn->next) {
  250. if ((conn->ThreadId == PR_MyThreadId()) && (conn->status == CB_CONNSTATUS_OK &&
  251. conn->refcount < maxconcurrency)){
  252. if (cb_debug_on()) {
  253. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  254. "<= cb_get_connection server found conn 0x%x to use)\n", conn );
  255. }
  256. goto unlock_and_return; /* found one */
  257. }
  258. }
  259. }
  260. else {
  261. connprev = NULL;
  262. for ( conn = pool->conn.conn_list; conn != NULL; conn = conn->next ) {
  263. if (cb_debug_on()) {
  264. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  265. "list: conn 0x%x status %d refcount %d\n", conn,
  266. conn->status, conn->refcount );
  267. }
  268. if ( conn->status == CB_CONNSTATUS_OK
  269. && conn->refcount < maxconcurrency ) {
  270. if (cb_debug_on()) {
  271. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  272. "<= cb_get_connection server found conn 0x%x to use)\n", conn );
  273. }
  274. goto unlock_and_return; /* found one */
  275. }
  276. connprev = conn;
  277. }
  278. }
  279. if ( secure || pool->conn.conn_list_count <maxconnections) {
  280. int version=LDAP_VERSION3;
  281. /* check wether the security libraries are correctly initialized */
  282. if (secure && slapd_security_library_is_initialized() != 1) {
  283. slapi_log_error(
  284. SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
  285. "SSL Not Initialized, Chaining Backend over SSL FAILED\n");
  286. rc = LDAP_CONNECT_ERROR;
  287. goto unlock_and_return;
  288. }
  289. /*
  290. * we have not exceeded the maximum number of connections allowed,
  291. * so we initialize a new one and add it to the end of our list.
  292. */
  293. /* No need to lock. url can't be changed dynamically */
  294. if ((ld=slapi_ldap_init(hostname,port,secure,isMultiThread))== NULL) {
  295. if (cb_debug_on()) {
  296. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  297. "Can't contact server <%s> port <%d>.\n", hostname, port);
  298. }
  299. if ( errmsg ) {
  300. *errmsg = PR_smprintf(error1,"unknown reason");
  301. }
  302. rc = LDAP_CONNECT_ERROR;
  303. goto unlock_and_return;
  304. }
  305. ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
  306. /* Don't chase referrals */
  307. ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF );
  308. /* no controls and simple bind only */
  309. /* For now, bind even if no user to detect error */
  310. /* earlier */
  311. if (pool->bindit) {
  312. PRErrorCode prerr = 0;
  313. LDAPControl **serverctrls=NULL;
  314. char *plain = NULL;
  315. int ret = -1;
  316. rc=LDAP_SUCCESS;
  317. if (cb_debug_on()) {
  318. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  319. "Bind to to server <%s> port <%d> as <%s>\n",
  320. hostname, port, binddn);
  321. }
  322. ret = pw_rever_decode(password, &plain, CB_CONFIG_USERPASSWORD);
  323. /* Pb occured in decryption: stop now, binding will fail */
  324. if ( ret == -1 )
  325. {
  326. if (cb_debug_on()) {
  327. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  328. "Internal credentials decoding error\n.",
  329. 0, 0, 0);
  330. }
  331. rc = LDAP_LOCAL_ERROR;
  332. goto unlock_and_return;
  333. }
  334. /* Password-based client authentication */
  335. rc = slapi_ldap_bind(ld, binddn, plain, mech, NULL, &serverctrls,
  336. &bind_to, NULL);
  337. if ( ret == 0 ) slapi_ch_free_string(&plain); /* free plain only if it has been duplicated */
  338. if ( rc == LDAP_TIMEOUT ) {
  339. if (cb_debug_on()) {
  340. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  341. "Can't bind to server <%s> port <%d>. (%s)\n",
  342. hostname, port, "time-out expired");
  343. }
  344. rc = LDAP_CONNECT_ERROR;
  345. goto unlock_and_return;
  346. } else if ( rc != LDAP_SUCCESS ) {
  347. prerr=PR_GetError();
  348. if (cb_debug_on()) {
  349. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  350. "Can't bind to server <%s> port <%d>. "
  351. "(LDAP error %d - %s; "
  352. SLAPI_COMPONENT_NAME_NSPR " error %d - %s)\n",
  353. hostname, port, rc,
  354. ldap_err2string(rc),
  355. prerr, slapd_pr_strerror(prerr));
  356. }
  357. if ( errmsg ) {
  358. *errmsg = PR_smprintf(error2, ldap_err2string(rc));
  359. }
  360. rc = LDAP_CONNECT_ERROR;
  361. goto unlock_and_return;
  362. }
  363. if ( serverctrls )
  364. {
  365. int i;
  366. for( i = 0; serverctrls[ i ] != NULL; ++i )
  367. {
  368. if ( !(strcmp( serverctrls[ i ]->ldctl_oid, LDAP_CONTROL_PWEXPIRED)) )
  369. {
  370. /* Bind is successful but password has expired */
  371. slapi_log_error(SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
  372. "Succesfully bound as %s to remote server %s:%d, "
  373. "but password has expired.\n",
  374. binddn, hostname, port);
  375. }
  376. else if ( !(strcmp( serverctrls[ i ]->ldctl_oid, LDAP_CONTROL_PWEXPIRING)) )
  377. {
  378. /* The password is expiring in n seconds */
  379. if ( (serverctrls[ i ]->ldctl_value.bv_val != NULL) &&
  380. (serverctrls[ i ]->ldctl_value.bv_len > 0) )
  381. {
  382. int password_expiring = atoi( serverctrls[ i ]->ldctl_value.bv_val );
  383. slapi_log_error(SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
  384. "Succesfully bound as %s to remote server %s:%d, "
  385. "but password is expiring in %d seconds.\n",
  386. binddn, hostname, port, password_expiring);
  387. }
  388. }
  389. }
  390. ldap_controls_free(serverctrls);
  391. }
  392. }
  393. conn = (cb_outgoing_conn *) slapi_ch_malloc(sizeof(cb_outgoing_conn));
  394. conn->ld=ld;
  395. conn->status=CB_CONNSTATUS_OK;
  396. conn->refcount=0; /* incremented below */
  397. conn->opentime=current_time();
  398. conn->ThreadId=PR_MyThreadId(); /* store the thread id */
  399. conn->next=NULL;
  400. if (secure) {
  401. if (pool->connarray[PR_ThreadSelf()] == NULL) {
  402. pool->connarray[PR_ThreadSelf()] = conn;
  403. }
  404. else {
  405. conn->next = pool->connarray[PR_ThreadSelf()];
  406. pool->connarray[PR_ThreadSelf()] = conn ;
  407. }
  408. }
  409. else {
  410. if ( NULL == connprev ) {
  411. pool->conn.conn_list = conn;
  412. } else {
  413. connprev->next=conn;
  414. }
  415. }
  416. ++pool->conn.conn_list_count;
  417. if (cb_debug_on()) {
  418. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  419. "<= cb_get_connection added new conn 0x%x, "
  420. "conn count now %d\n", conn->ld, pool->conn.conn_list_count );
  421. }
  422. goto unlock_and_return; /* got a new one */
  423. }
  424. if (cb_debug_on()) {
  425. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  426. "... cb_get_connection waiting for conn to free up\n" );
  427. }
  428. if (!secure) slapi_wait_condvar( pool->conn.conn_list_cv, NULL );
  429. if (cb_debug_on()) {
  430. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  431. "... cb_get_connection awake again\n" );
  432. }
  433. }
  434. unlock_and_return:
  435. if ( conn != NULL ) {
  436. ++conn->refcount;
  437. *lld=conn->ld;
  438. *cc=conn;
  439. if (cb_debug_on()) {
  440. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  441. "<== cb_get_connection ld=0x%x (concurrency now %d)\n",*lld, conn->refcount );
  442. }
  443. } else {
  444. if ( NULL != ld ) {
  445. slapi_ldap_unbind( ld );
  446. }
  447. if (cb_debug_on()) {
  448. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  449. "<== cb_get_connection error %d\n", rc );
  450. }
  451. }
  452. slapi_unlock_mutex(pool->conn.conn_list_mutex);
  453. return( rc );
  454. }
  455. /*
  456. * We are done with the connection handle because the
  457. * LDAP operation has completed.
  458. */
  459. void cb_release_op_connection(cb_conn_pool* pool, LDAP *lld, int dispose) {
  460. cb_outgoing_conn *conn;
  461. cb_outgoing_conn *connprev = NULL;
  462. int secure = pool->secure;
  463. int myself = 0;
  464. slapi_lock_mutex(pool->conn.conn_list_mutex);
  465. /*
  466. * find the connection structure this ld is part of
  467. */
  468. if (secure) {
  469. myself = PR_ThreadSelf();
  470. for (conn = pool->connarray[myself]; conn != NULL; conn = conn->next ) {
  471. if ( lld == conn->ld )
  472. break;
  473. connprev = conn;
  474. }
  475. }
  476. else {
  477. for ( conn = pool->conn.conn_list; conn != NULL; conn = conn->next ){
  478. if ( lld == conn->ld )
  479. break;
  480. connprev = conn;
  481. }
  482. }
  483. if ( conn == NULL ) { /* ld not found -- unexpected */
  484. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  485. "==> cb_release_op_connection ld=0x%x not found\n", lld );
  486. } else {
  487. --conn->refcount;
  488. if (cb_debug_on()) {
  489. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  490. "release conn 0x%x status %d refcount after release %d\n", conn,
  491. conn->status, conn->refcount );
  492. }
  493. if ( dispose ) {
  494. conn->status = CB_CONNSTATUS_DOWN;
  495. }
  496. if ( conn->status != CB_CONNSTATUS_OK && conn->refcount == 0 ) {
  497. /*
  498. * remove from server's connection list
  499. */
  500. if (!secure) {
  501. if ( connprev == NULL ) {
  502. pool->conn.conn_list = conn->next;
  503. } else {
  504. connprev->next = conn->next;
  505. }
  506. }
  507. else {
  508. if ( connprev == NULL ) {
  509. pool->connarray[myself] = conn->next;
  510. } else {
  511. connprev->next = conn->next;
  512. }
  513. }
  514. --pool->conn.conn_list_count;
  515. /*
  516. * close connection and free memory
  517. */
  518. cb_close_and_dispose_connection( conn );
  519. }
  520. }
  521. /*
  522. * wake up a thread that is waiting for a connection
  523. */
  524. if (!secure) slapi_notify_condvar( pool->conn.conn_list_cv, 0 );
  525. slapi_unlock_mutex( pool->conn.conn_list_mutex );
  526. }
  527. static void
  528. cb_close_and_dispose_connection( cb_outgoing_conn *conn )
  529. {
  530. slapi_ldap_unbind( conn->ld );
  531. conn->ld = NULL;
  532. slapi_ch_free( (void **)&conn );
  533. }
  534. static void cb_check_for_stale_connections(cb_conn_pool * pool) {
  535. cb_outgoing_conn * connprev, *conn, *conn_next;
  536. time_t curtime;
  537. int connlifetime;
  538. int myself;
  539. PR_RWLock_Rlock(pool->rwl_config_lock);
  540. connlifetime=pool->conn.connlifetime;
  541. PR_RWLock_Unlock(pool->rwl_config_lock);
  542. connprev = NULL;
  543. conn_next = NULL;
  544. slapi_lock_mutex(pool->conn.conn_list_mutex);
  545. if (connlifetime > 0)
  546. curtime=current_time();
  547. if (pool->secure) {
  548. myself = PR_ThreadSelf();
  549. for (conn = pool->connarray[myself]; conn != NULL; conn = conn_next){
  550. if ((conn->status == CB_CONNSTATUS_STALE) ||
  551. (( connlifetime > 0) && (curtime - conn->opentime > connlifetime))) {
  552. if ( conn->refcount == 0 ) {
  553. if (cb_debug_on()) {
  554. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  555. "cb_check_for_stale_connections: conn 0x%x idle and stale\n",conn);
  556. }
  557. --pool->conn.conn_list_count;
  558. if (connprev == NULL) {
  559. pool->connarray[myself] = conn->next ;
  560. }
  561. else {
  562. connprev->next = conn->next ;
  563. }
  564. conn_next = conn->next ;
  565. cb_close_and_dispose_connection( conn );
  566. continue;
  567. }
  568. /* Connection is stale but in use */
  569. /* Mark to be disposed later but let it in the backend list */
  570. /* so that it is counted as a valid connection */
  571. else {
  572. conn->status = CB_CONNSTATUS_STALE;
  573. }
  574. if (cb_debug_on()) {
  575. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  576. "cb_check_for_stale_connections: conn 0x%x stale\n",conn);
  577. }
  578. }
  579. connprev = conn ;
  580. conn_next = conn->next;
  581. }
  582. slapi_unlock_mutex(pool->conn.conn_list_mutex);
  583. return;
  584. }
  585. for ( conn = pool->conn.conn_list; conn != NULL; conn=conn_next ) {
  586. if ((conn->status == CB_CONNSTATUS_STALE) ||
  587. (( connlifetime > 0) && (curtime - conn->opentime > connlifetime))) {
  588. if ( conn->refcount == 0 ) {
  589. /* Connection idle & stale. Remove and free. */
  590. if ( NULL == connprev )
  591. pool->conn.conn_list = conn->next;
  592. else
  593. connprev->next=conn->next;
  594. if (cb_debug_on()) {
  595. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  596. "cb_check_for_stale_connections: conn 0x%x idle and stale\n",conn);
  597. }
  598. --pool->conn.conn_list_count;
  599. conn_next=conn->next;
  600. cb_close_and_dispose_connection( conn );
  601. continue;
  602. }
  603. /* Connection is stale but in use */
  604. /* Mark to be disposed later but let it in the backend list */
  605. /* so that it is counted as a valid connection */
  606. else {
  607. conn->status = CB_CONNSTATUS_STALE;
  608. }
  609. if (cb_debug_on()) {
  610. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  611. "cb_check_for_stale_connections: conn 0x%x stale\n",conn);
  612. }
  613. }
  614. connprev = conn;
  615. conn_next=conn->next;
  616. }
  617. /* Generate an event to wake up threads waiting */
  618. /* for a conn to be released. Useful to detect */
  619. /* exceeded time limit. May be expensive */
  620. slapi_notify_condvar( pool->conn.conn_list_cv, 0 );
  621. slapi_unlock_mutex(pool->conn.conn_list_mutex);
  622. }
  623. /*
  624. * close all open connections in preparation for server shutdown, etc.
  625. * WARNING: Don't wait for current operations to complete
  626. */
  627. void
  628. cb_close_all_connections( Slapi_Backend * be )
  629. {
  630. cb_outgoing_conn *conn, *next_conn;
  631. cb_backend_instance * cb= cb_get_instance(be);
  632. int i;
  633. slapi_lock_mutex(cb->pool->conn.conn_list_mutex);
  634. if (cb->pool->secure) {
  635. for (i=0; i< MAX_CONN_ARRAY; i++) {
  636. for (conn = cb->pool->connarray[i]; conn != NULL; conn = next_conn ){
  637. next_conn = conn->next;
  638. cb_close_and_dispose_connection(conn);
  639. }
  640. }
  641. } else {
  642. for ( conn = cb->pool->conn.conn_list; conn != NULL; conn = next_conn ) {
  643. next_conn=conn->next;
  644. cb_close_and_dispose_connection(conn);
  645. }
  646. }
  647. slapi_unlock_mutex(cb->pool->conn.conn_list_mutex);
  648. slapi_lock_mutex(cb->bind_pool->conn.conn_list_mutex);
  649. if (cb->bind_pool->secure) {
  650. for (i=0; i< MAX_CONN_ARRAY; i++) {
  651. for (conn = cb->bind_pool->connarray[i]; conn != NULL; conn = next_conn ){
  652. next_conn=conn->next;
  653. cb_close_and_dispose_connection(conn);
  654. }
  655. }
  656. } else {
  657. for ( conn = cb->bind_pool->conn.conn_list; conn != NULL; conn = next_conn ) {
  658. next_conn=conn->next;
  659. cb_close_and_dispose_connection(conn);
  660. }
  661. }
  662. slapi_unlock_mutex(cb->bind_pool->conn.conn_list_mutex);
  663. }
  664. /* Mark used connections as stale and close unsued connections */
  665. /* Called when the target farm url has changed */
  666. void cb_stale_all_connections( cb_backend_instance * cb)
  667. {
  668. cb_outgoing_conn *conn, *next_conn, *prev_conn;
  669. int notify=0;
  670. int i, j;
  671. cb_conn_pool *pools[3];
  672. pools[0]=cb->pool;
  673. pools[1]=cb->bind_pool;
  674. pools[2]=NULL;
  675. for (i=0; pools[i]; i++) {
  676. slapi_lock_mutex(pools[i]->conn.conn_list_mutex);
  677. for (j=0; j< MAX_CONN_ARRAY; j++) {
  678. prev_conn=NULL;
  679. for (conn = pools[i]->connarray[j]; conn != NULL; conn=next_conn) {
  680. next_conn=conn->next;
  681. if (conn->refcount > 0) {
  682. /*
  683. ** Connection is stale but in use
  684. ** Mark to be disposed later but let it in the backend list
  685. ** so that it is counted as a valid connection
  686. */
  687. conn->status = CB_CONNSTATUS_STALE;
  688. prev_conn=conn;
  689. } else {
  690. if (prev_conn == NULL) {
  691. pools[i]->connarray[j]=next_conn;
  692. } else {
  693. prev_conn->next=next_conn;
  694. }
  695. cb_close_and_dispose_connection(conn);
  696. pools[i]->conn.conn_list_count--;
  697. }
  698. }
  699. }
  700. prev_conn = NULL ;
  701. for ( conn = pools[i]->conn.conn_list; conn != NULL; conn = next_conn ) {
  702. next_conn=conn->next;
  703. if (conn->refcount > 0) {
  704. /*
  705. ** Connection is stale but in use
  706. ** Mark to be disposed later but let it in the backend list
  707. ** so that it is counted as a valid connection
  708. */
  709. conn->status = CB_CONNSTATUS_STALE;
  710. prev_conn=conn;
  711. }
  712. else {
  713. if (conn==pools[i]->conn.conn_list) {
  714. pools[i]->conn.conn_list=next_conn;
  715. } else {
  716. prev_conn->next=next_conn;
  717. }
  718. cb_close_and_dispose_connection(conn);
  719. pools[i]->conn.conn_list_count--;
  720. notify=1;
  721. }
  722. }
  723. if (notify && (! pools[i]->secure)) {
  724. slapi_notify_condvar( pools[i]->conn.conn_list_cv, 0 );
  725. }
  726. slapi_unlock_mutex(pools[i]->conn.conn_list_mutex);
  727. }
  728. }
  729. /* Try to figure out if a farm server is still alive */
  730. int cb_ping_farm(cb_backend_instance *cb, cb_outgoing_conn * cnx,time_t end_time) {
  731. char *attrs[] ={"1.1",NULL};
  732. int rc;
  733. struct timeval timeout;
  734. LDAP *ld;
  735. LDAPMessage *result;
  736. time_t now;
  737. int secure;
  738. if (cb->max_idle_time <=0) /* Heart-beat disabled */
  739. return LDAP_SUCCESS;
  740. if (cnx && (cnx->status != CB_CONNSTATUS_OK )) /* Known problem */
  741. return LDAP_SERVER_DOWN;
  742. now = current_time();
  743. if (end_time && ((now <= end_time) || (end_time <0))) return LDAP_SUCCESS;
  744. secure = cb->pool->secure;
  745. if (cb->pool->starttls) {
  746. secure = 2;
  747. }
  748. ld=slapi_ldap_init(cb->pool->hostname,cb->pool->port,secure,0);
  749. if (NULL == ld) {
  750. cb_update_failed_conn_cpt( cb );
  751. return LDAP_SERVER_DOWN;
  752. }
  753. timeout.tv_sec=cb->max_test_time;
  754. timeout.tv_usec=0;
  755. /* NOTE: This will fail if we implement the ability to disable
  756. anonymous bind */
  757. rc=ldap_search_ext_s(ld ,NULL,LDAP_SCOPE_BASE,"objectclass=*",attrs,1,NULL,
  758. NULL, &timeout, 1,&result);
  759. if ( LDAP_SUCCESS != rc ) {
  760. slapi_ldap_unbind( ld );
  761. cb_update_failed_conn_cpt( cb );
  762. return LDAP_SERVER_DOWN;
  763. }
  764. ldap_msgfree(result);
  765. slapi_ldap_unbind( ld );
  766. cb_reset_conn_cpt( cb );
  767. return LDAP_SUCCESS;
  768. }
  769. void cb_update_failed_conn_cpt ( cb_backend_instance *cb ) {
  770. /* if the chaining BE is already unavailable, we do nothing*/
  771. time_t now;
  772. if (cb->monitor_availability.farmserver_state == FARMSERVER_AVAILABLE) {
  773. slapi_lock_mutex(cb->monitor_availability.cpt_lock);
  774. cb->monitor_availability.cpt ++;
  775. slapi_unlock_mutex(cb->monitor_availability.cpt_lock);
  776. if (cb->monitor_availability.cpt >= CB_NUM_CONN_BEFORE_UNAVAILABILITY ) {
  777. /* we reach the limit of authorized failed connections => we setup the chaining BE state to unavailable */
  778. now = current_time();
  779. slapi_lock_mutex(cb->monitor_availability.lock_timeLimit);
  780. cb->monitor_availability.unavailableTimeLimit = now + CB_UNAVAILABLE_PERIOD ;
  781. slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
  782. cb->monitor_availability.farmserver_state = FARMSERVER_UNAVAILABLE ;
  783. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  784. "cb_update_failed_conn_cpt: Farm server unavailable");
  785. }
  786. }
  787. }
  788. void cb_reset_conn_cpt( cb_backend_instance *cb ) {
  789. if (cb->monitor_availability.cpt > 0) {
  790. slapi_lock_mutex(cb->monitor_availability.cpt_lock);
  791. cb->monitor_availability.cpt = 0 ;
  792. if (cb->monitor_availability.farmserver_state == FARMSERVER_UNAVAILABLE) {
  793. cb->monitor_availability.farmserver_state = FARMSERVER_AVAILABLE ;
  794. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  795. "cb_reset_conn_cpt: Farm server is back");
  796. }
  797. slapi_unlock_mutex(cb->monitor_availability.cpt_lock);
  798. }
  799. }
  800. int cb_check_availability( cb_backend_instance *cb, Slapi_PBlock *pb ) {
  801. /* check wether the farmserver is available or not */
  802. time_t now ;
  803. if ( cb->monitor_availability.farmserver_state == FARMSERVER_UNAVAILABLE ){
  804. slapi_lock_mutex(cb->monitor_availability.lock_timeLimit);
  805. now = current_time();
  806. if (now >= cb->monitor_availability.unavailableTimeLimit) {
  807. cb->monitor_availability.unavailableTimeLimit = now + CB_INFINITE_TIME ; /* to be sure only one thread can do the test */
  808. slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
  809. }
  810. else {
  811. slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
  812. cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL) ;
  813. return FARMSERVER_UNAVAILABLE ;
  814. }
  815. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  816. "cb_check_availability: ping the farm server and check if it's still unavailable");
  817. if (cb_ping_farm(cb, NULL, 0) != LDAP_SUCCESS) { /* farm still unavailable... Just change the timelimit */
  818. slapi_lock_mutex(cb->monitor_availability.lock_timeLimit);
  819. now = current_time();
  820. cb->monitor_availability.unavailableTimeLimit = now + CB_UNAVAILABLE_PERIOD ;
  821. slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
  822. cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL) ;
  823. slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
  824. "cb_check_availability: Farm server still unavailable");
  825. return FARMSERVER_UNAVAILABLE ;
  826. }
  827. else {
  828. /* farm is back !*/
  829. slapi_lock_mutex(cb->monitor_availability.lock_timeLimit);
  830. now = current_time();
  831. cb->monitor_availability.unavailableTimeLimit = now ; /* the unavailable period is finished */
  832. slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
  833. /* The farmer server state backs to FARMSERVER_AVAILABLE, but this already done in cb_ping_farm, and also the reset of cpt*/
  834. return FARMSERVER_AVAILABLE ;
  835. }
  836. }
  837. return FARMSERVER_AVAILABLE ;
  838. }