conntable.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  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. /* Connection Table */
  39. #include "fe.h"
  40. Connection_Table *
  41. connection_table_new(int table_size)
  42. {
  43. Connection_Table *ct;
  44. int i = 0;
  45. ct= (Connection_Table*)slapi_ch_calloc( 1, sizeof(Connection_Table) );
  46. ct->size= table_size;
  47. ct->c = (Connection *)slapi_ch_calloc( 1, table_size * sizeof(Connection) );
  48. ct->fd = (struct POLL_STRUCT *)slapi_ch_calloc(1, table_size * sizeof(struct POLL_STRUCT));
  49. ct->table_mutex = PR_NewLock();
  50. /* We rely on the fact that we called calloc, which zeros the block, so we don't
  51. * init any structure element unless a zero value is troublesome later
  52. */
  53. for ( i = 0; i < table_size; i++ )
  54. {
  55. int invalid_socket;
  56. unsigned long maxbersize= config_get_maxbersize();
  57. /* DBDB---move this out of here once everything works */
  58. ct->c[i].c_sb = ber_sockbuf_alloc();
  59. invalid_socket = SLAPD_INVALID_SOCKET;
  60. ber_sockbuf_set_option( ct->c[i].c_sb, LBER_SOCKBUF_OPT_DESC, &invalid_socket );
  61. ct->c[i].c_sd = SLAPD_INVALID_SOCKET;
  62. ber_sockbuf_set_option( ct->c[i].c_sb, LBER_SOCKBUF_OPT_NO_READ_AHEAD, LBER_OPT_ON );
  63. ber_sockbuf_set_option( ct->c[i].c_sb, LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE, &maxbersize );
  64. #ifndef _WIN32
  65. /* all connections start out invalid */
  66. ct->fd[i].fd = SLAPD_INVALID_SOCKET;
  67. #endif
  68. /* The connection table has a double linked list running through it.
  69. * This is used to find out which connections should be looked at
  70. * in the poll loop. Slot 0 in the table is always the head of
  71. * the linked list. Each slot has a c_next and c_prev which are
  72. * pointers back into the array of connection slots. */
  73. ct->c[i].c_next = NULL;
  74. ct->c[i].c_prev = NULL;
  75. ct->c[i].c_ci= i;
  76. ct->c[i].c_fdi= SLAPD_INVALID_SOCKET_INDEX;
  77. }
  78. return ct;
  79. }
  80. void connection_table_free(Connection_Table *ct)
  81. {
  82. int i;
  83. for (i = 0; i < ct->size; i++)
  84. {
  85. /* Free the contents of the connection structure */
  86. Connection *c= &(ct->c[i]);
  87. connection_done(c);
  88. }
  89. slapi_ch_free((void**)&ct->c);
  90. slapi_ch_free((void**)&ct->fd);
  91. PR_DestroyLock(ct->table_mutex);
  92. slapi_ch_free((void**)&ct);
  93. }
  94. #ifdef _WIN32
  95. /*
  96. * This function looks up connection by nspr fd. It is
  97. * slow because it iterrates through the entire connection
  98. * array. Currently, it is only used on NT in secure_read_function
  99. * to handle I/O timeout on SSL socket we should almost never
  100. * happen.
  101. */
  102. Connection*
  103. connection_table_get_connection_from_fd(Connection_Table *ct,PRFileDesc *prfd)
  104. {
  105. int i;
  106. for (i = 0; i < ct->size; i++)
  107. {
  108. if (ct->c[i].c_prfd == prfd)
  109. {
  110. return (&(ct->c[i]));
  111. }
  112. }
  113. return NULL;
  114. }
  115. #endif
  116. void
  117. connection_table_abandon_all_operations(Connection_Table *ct)
  118. {
  119. int i;
  120. for ( i = 0; i < ct->size; i++ )
  121. {
  122. if ( ct->c[i].c_mutex != NULL )
  123. {
  124. PR_Lock( ct->c[i].c_mutex );
  125. connection_abandon_operations( &ct->c[i] );
  126. PR_Unlock( ct->c[i].c_mutex );
  127. }
  128. }
  129. }
  130. /* Given a file descriptor for a socket, this function will return
  131. * a slot in the connection table to use.
  132. *
  133. * Note: this function is only called from the slapd_daemon (listener)
  134. * thread, which means it will never be called by two threads at
  135. * the same time.
  136. *
  137. * Returns a Connection on success
  138. * Returns NULL on failure
  139. */
  140. Connection *
  141. connection_table_get_connection(Connection_Table *ct, int sd)
  142. {
  143. Connection *c= NULL;
  144. int index, count;
  145. index = sd % ct->size;
  146. for( count = 0; count < ct->size; count++, index = (index + 1) % ct->size)
  147. {
  148. /* Do not use slot 0, slot 0 is head of the list of active connections */
  149. if ( index == 0 )
  150. {
  151. continue;
  152. }
  153. else if( ct->c[index].c_mutex == NULL )
  154. {
  155. break;
  156. }
  157. if( connection_is_free (&(ct->c[index])))
  158. {
  159. break;
  160. }
  161. }
  162. if ( count < ct->size )
  163. {
  164. /* Found an available Connection */
  165. c= &(ct->c[index]);
  166. PR_ASSERT(c->c_next==NULL);
  167. PR_ASSERT(c->c_prev==NULL);
  168. PR_ASSERT(c->c_extension==NULL);
  169. if ( c->c_mutex == NULL )
  170. {
  171. PR_Lock( ct->table_mutex );
  172. c->c_mutex = PR_NewLock();
  173. c->c_pdumutex = PR_NewLock();
  174. PR_Unlock( ct->table_mutex );
  175. if ( c->c_mutex == NULL || c->c_pdumutex == NULL )
  176. {
  177. c->c_mutex = NULL;
  178. c->c_pdumutex = NULL;
  179. LDAPDebug( LDAP_DEBUG_ANY,"PR_NewLock failed\n",0, 0, 0 );
  180. c= NULL;
  181. }
  182. }
  183. /* Let's make sure there's no cruft left on there from the last time this connection was used. */
  184. /* Note: no need to lock c->c_mutex because this function is only
  185. * called by one thread (the slapd_daemon thread), and if we got this
  186. * far then `c' is not being used by any operation threads, etc.
  187. */
  188. connection_cleanup(c);
  189. }
  190. else
  191. {
  192. /* couldn't find a Connection */
  193. LDAPDebug( LDAP_DEBUG_CONNS, "max open connections reached\n", 0, 0, 0);
  194. }
  195. return c;
  196. }
  197. /* active connection iteration functions */
  198. Connection*
  199. connection_table_get_first_active_connection (Connection_Table *ct)
  200. {
  201. return ct->c[0].c_next;
  202. }
  203. Connection*
  204. connection_table_get_next_active_connection (Connection_Table *ct, Connection *c)
  205. {
  206. return c->c_next;
  207. }
  208. int connection_table_iterate_active_connections(Connection_Table *ct, void* arg, Connection_Table_Iterate_Function f)
  209. {
  210. /* Locking in this area seems rather undeveloped, I think because typically only one
  211. * thread accesses the connection table (daemon thread). But now we allow other threads
  212. * to iterate over the table. So we use the "new mutex mutex" to lock the table.
  213. */
  214. Connection *current_conn = NULL;
  215. int ret = 0;
  216. PR_Lock(ct->table_mutex);
  217. current_conn = connection_table_get_first_active_connection (ct);
  218. while (current_conn) {
  219. ret = f(current_conn, arg);
  220. if (ret) {
  221. break;
  222. }
  223. current_conn = connection_table_get_next_active_connection (ct, current_conn);
  224. }
  225. PR_Unlock(ct->table_mutex);
  226. return ret;
  227. }
  228. static void
  229. connection_table_dump_active_connection (Connection *c)
  230. {
  231. slapi_log_error(SLAPI_LOG_FATAL, "connection", "conn=%p fd=%d refcnt=%d c_flags=%d\n"
  232. "mutex=%p next=%p prev=%p\n\n", c, c->c_sd, c->c_refcnt, c->c_flags,
  233. c->c_mutex, c->c_next, c->c_prev);
  234. }
  235. #if 0 /* useful for debugging */
  236. static void
  237. connection_table_dump_active_connections (Connection_Table *ct)
  238. {
  239. Connection* c;
  240. PR_Lock(ct->table_mutex);
  241. slapi_log_error(SLAPI_LOG_FATAL, "connection", "********** BEGIN DUMP ************\n");
  242. c = connection_table_get_first_active_connection (ct);
  243. while (c)
  244. {
  245. connection_table_dump_active_connection (c);
  246. c = connection_table_get_next_active_connection (ct, c);
  247. }
  248. slapi_log_error(SLAPI_LOG_FATAL, "connection", "********** END DUMP ************\n");
  249. PR_Unlock(ct->table_mutex);
  250. }
  251. #endif
  252. /*
  253. * There's a double linked list of active connections running through the array
  254. * of connections. This function removes a connection (by index) from that
  255. * list. This list is used to find the connections that should be used in the
  256. * poll call.
  257. */
  258. void
  259. connection_table_move_connection_out_of_active_list(Connection_Table *ct,Connection *c)
  260. {
  261. /* we always have previous element because list contains a dummy header */;
  262. PR_ASSERT (c->c_prev);
  263. /* slapi_log_error(SLAPI_LOG_FATAL, "connection", "Moving connection out of active list\n");
  264. connection_table_dump_active_connection (c);*/
  265. /*
  266. * Note: the connection will NOT be moved off the active list if any other threads still hold
  267. * a reference to the connection (that is, its reference count must be 1 or less).
  268. */
  269. if(c->c_refcnt > 1) return;
  270. /* We need to lock here because we're modifying the linked list */
  271. PR_Lock(ct->table_mutex);
  272. c->c_prev->c_next = c->c_next;
  273. if (c->c_next)
  274. {
  275. c->c_next->c_prev = c->c_prev;
  276. }
  277. connection_release_nolock (c);
  278. connection_cleanup (c);
  279. PR_Unlock(ct->table_mutex);
  280. /* connection_table_dump_active_connections (ct); */
  281. }
  282. /*
  283. * There's a double linked list of active connections running through the array
  284. * of connections. This function adds a connection (by index) to the head of
  285. * that list. This list is used to find the connections that should be used in the
  286. * poll call.
  287. */
  288. void
  289. connection_table_move_connection_on_to_active_list(Connection_Table *ct,Connection *c)
  290. {
  291. PR_ASSERT(c->c_next==NULL);
  292. PR_ASSERT(c->c_prev==NULL);
  293. PR_Lock(ct->table_mutex);
  294. connection_acquire_nolock (c);
  295. /* slapi_log_error(SLAPI_LOG_FATAL, "connection", "Moving connection into active list\n");
  296. connection_table_dump_active_connection (c);*/
  297. c->c_next = ct->c[0].c_next;
  298. if ( c->c_next != NULL )
  299. {
  300. c->c_next->c_prev = c;
  301. }
  302. c->c_prev = &(ct->c[0]);
  303. ct->c[0].c_next = c;
  304. PR_Unlock(ct->table_mutex);
  305. /* connection_table_dump_active_connections (ct); */
  306. }
  307. /*
  308. * Replace the following attributes within the entry 'e' with
  309. * information about the connection table:
  310. * connection // multivalued; one value for each active connection
  311. * currentconnections // single valued; an integer count
  312. * totalconnections // single valued; an integer count
  313. * dtablesize // single valued; an integer size
  314. * readwaiters // single valued; an integer count
  315. */
  316. void
  317. connection_table_as_entry(Connection_Table *ct, Slapi_Entry *e)
  318. {
  319. char buf[BUFSIZ];
  320. struct berval val;
  321. struct berval *vals[2];
  322. int i, nconns, nreadwaiters;
  323. struct tm utm;
  324. vals[0] = &val;
  325. vals[1] = NULL;
  326. attrlist_delete( &e->e_attrs, "connection");
  327. nconns = 0;
  328. nreadwaiters = 0;
  329. for ( i = 0; i < (ct!=NULL?ct->size:0); i++ )
  330. {
  331. PR_Lock( ct->table_mutex );
  332. if ( (ct->c[i].c_mutex == NULL) || (ct->c[i].c_mutex == (PRLock*)-1) )
  333. {
  334. PR_Unlock( ct->table_mutex );
  335. continue;
  336. }
  337. /* Can't take c_mutex if holding table_mutex; temporarily unlock */
  338. PR_Unlock( ct->table_mutex );
  339. PR_Lock( ct->c[i].c_mutex );
  340. if ( ct->c[i].c_sd != SLAPD_INVALID_SOCKET )
  341. {
  342. char buf2[20];
  343. int lendn = ct->c[i].c_dn ? strlen(ct->c[i].c_dn) : 6; /* "NULLDN" */
  344. char *bufptr = &buf[0];
  345. char *newbuf = NULL;
  346. nconns++;
  347. if ( ct->c[i].c_gettingber )
  348. {
  349. nreadwaiters++;
  350. }
  351. #ifdef _WIN32
  352. {
  353. struct tm *pt;
  354. pt = gmtime( &ct->c[i].c_starttime );
  355. memcpy(&utm, pt, sizeof(struct tm) );
  356. }
  357. #else
  358. gmtime_r( &ct->c[i].c_starttime, &utm );
  359. #endif
  360. strftime( buf2, sizeof(buf2), "%Y%m%d%H%M%SZ", &utm );
  361. if (lendn > (BUFSIZ - 46)) {
  362. /*
  363. * 46 = 4 for the "i" couter + 20 for buf2 +
  364. * 10 for c_opsinitiated + 10 for c_opscompleted +
  365. * 1 for c_gettingber + 1
  366. */
  367. newbuf = (char *) slapi_ch_malloc(lendn + 46);
  368. bufptr = newbuf;
  369. }
  370. sprintf( bufptr, "%d:%s:%d:%d:%s%s:%s", i,
  371. buf2,
  372. ct->c[i].c_opsinitiated,
  373. ct->c[i].c_opscompleted,
  374. ct->c[i].c_gettingber ? "r" : "",
  375. "",
  376. ct->c[i].c_dn ? ct->c[i].c_dn : "NULLDN" );
  377. val.bv_val = bufptr;
  378. val.bv_len = strlen( bufptr );
  379. attrlist_merge( &e->e_attrs, "connection", vals );
  380. if (newbuf) {
  381. slapi_ch_free((void **) &newbuf);
  382. }
  383. }
  384. PR_Unlock( ct->c[i].c_mutex );
  385. }
  386. sprintf( buf, "%d", nconns );
  387. val.bv_val = buf;
  388. val.bv_len = strlen( buf );
  389. attrlist_replace( &e->e_attrs, "currentconnections", vals );
  390. PR_Lock( num_conns_mutex );
  391. sprintf( buf, "%d", num_conns );
  392. PR_Unlock( num_conns_mutex );
  393. val.bv_val = buf;
  394. val.bv_len = strlen( buf );
  395. attrlist_replace( &e->e_attrs, "totalconnections", vals );
  396. sprintf( buf, "%d", (ct!=NULL?ct->size:0) );
  397. val.bv_val = buf;
  398. val.bv_len = strlen( buf );
  399. attrlist_replace( &e->e_attrs, "dtablesize", vals );
  400. sprintf( buf, "%d", nreadwaiters );
  401. val.bv_val = buf;
  402. val.bv_len = strlen( buf );
  403. attrlist_replace( &e->e_attrs, "readwaiters", vals );
  404. }
  405. void
  406. connection_table_dump_activity_to_errors_log(Connection_Table *ct)
  407. {
  408. int i;
  409. for ( i = 0; i < ct->size; i++ )
  410. {
  411. Connection *c= &(ct->c[i]);
  412. if ( c->c_mutex != NULL )
  413. {
  414. /* Find the connection we are referring to */
  415. int j= c->c_fdi;
  416. PR_Lock( c->c_mutex );
  417. if ( c->c_sd != SLAPD_INVALID_SOCKET && c->c_prfd == ct->fd[j].fd )
  418. {
  419. int r = ct->fd[j].out_flags & SLAPD_POLL_FLAGS;
  420. if ( r )
  421. {
  422. LDAPDebug( LDAP_DEBUG_CONNS,"activity on %d%s\n", i, r ? "r" : "",0 );
  423. }
  424. }
  425. PR_Unlock( c->c_mutex );
  426. }
  427. }
  428. }
  429. #if 0
  430. void dump_op_list(FILE *file, Operation *op);
  431. void
  432. connection_table_dump(Connection_Table *ct)
  433. {
  434. FILE *file;
  435. file = fopen("/tmp/slapd.conn", "a+");
  436. if (file != NULL)
  437. {
  438. int i;
  439. fprintf(file, "=============pid=%d==================\n", getpid());
  440. for ( i = 0; i < ct->size; i++ )
  441. {
  442. if ( (ct->c[i].c_sd == SLAPD_INVALID_SOCKET) && (ct->c[i].c_connid == 0) )
  443. {
  444. continue;
  445. }
  446. fprintf(file, "c[%d].c_dn=0x%x\n", i, ct->c[i].c_dn);
  447. dump_op_list(file, ct->c[i].c_ops);
  448. fprintf(file, "c[%d].c_sb.sb_sd=%d\n", i, ct->c[i].c_sd);
  449. fprintf(file, "c[%d].c_connid=%d\n", i, ct->c[i].c_connid);
  450. fprintf(file, "c[%d].c_opsinitiated=%d\n", i, ct->c[i].c_opsinitiated);
  451. fprintf(file, "c[%d].c_opscompleted=%d\n", i, ct->c[i].c_opscompleted);
  452. }
  453. fclose(file);
  454. }
  455. }
  456. static const char *
  457. op_status2str( int o_status )
  458. {
  459. const char *s = "unknown";
  460. switch( o_status ) {
  461. case SLAPI_OP_STATUS_PROCESSING:
  462. s = "processing";
  463. break;
  464. case SLAPI_OP_STATUS_ABANDONED:
  465. s = "abandoned";
  466. break;
  467. case SLAPI_OP_STATUS_WILL_COMPLETE:
  468. s = "will_complete";
  469. break;
  470. case SLAPI_OP_STATUS_RESULT_SENT:
  471. s = "result_sent";
  472. break;
  473. }
  474. return s;
  475. }
  476. void
  477. dump_op_list(FILE *file, Operation *op)
  478. {
  479. Operation *tmp;
  480. for ( tmp = op; tmp != NULL; tmp = tmp->o_next )
  481. {
  482. fprintf(file,
  483. "(o_msgid=%d, o_tag=%d, o_sdn=0x%x, o_opid=%d, o_connid=%d, o_status=%s)\n",
  484. tmp->o_msgid, tmp->o_tag, slapi_sdn_get_dn(&tmp->o_sdn), tmp->o_connid,
  485. *tmp->o_tid, op_status2str( tmp->o_status ));
  486. }
  487. }
  488. #endif /* 0 */