cb_conn_stateless.c 33 KB

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