searchthread.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  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) 2006 Red Hat, Inc.
  36. * All rights reserved.
  37. * END COPYRIGHT BLOCK **/
  38. #ifdef HAVE_CONFIG_H
  39. # include <config.h>
  40. #endif
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. #include <string.h>
  44. #ifdef XP_UNIX
  45. #include <unistd.h>
  46. #endif
  47. #include <time.h>
  48. #include <errno.h>
  49. #include "nspr.h"
  50. #include <sys/types.h>
  51. #include <sys/socket.h>
  52. #include <netinet/tcp.h> /* for TCP_NODELAY */
  53. #include "ldap.h"
  54. #include "rsearch.h"
  55. #include "searchthread.h"
  56. /* local data for a search thread */
  57. struct _searchthread {
  58. PRUint32 searchCount;
  59. PRUint32 failCount;
  60. double mintime;
  61. double maxtime;
  62. LDAP *ld;
  63. LDAP *ld2; /* aux LDAP handle */
  64. LBER_SOCKET soc;
  65. PRThread *tid;
  66. PRLock *lock;
  67. int id;
  68. int alive;
  69. int retry;
  70. };
  71. /* new searchthread */
  72. SearchThread *st_new(void)
  73. {
  74. SearchThread *st = (SearchThread *)malloc(sizeof(SearchThread));
  75. if (!st) return NULL;
  76. st->searchCount = st->failCount = 0;
  77. st->mintime = 10000;
  78. st->maxtime = 0;
  79. st->ld = NULL;
  80. st->ld2 = NULL;
  81. st->soc = -1;
  82. st->tid = NULL;
  83. st->id = 0;
  84. st->alive = 1;
  85. st->lock = PR_NewLock();
  86. st->retry = 0;
  87. srand(time(0));
  88. return st;
  89. }
  90. void st_setThread(SearchThread *st, PRThread *tid, int id)
  91. {
  92. st->tid = tid;
  93. st->id = id;
  94. }
  95. int st_getThread(SearchThread *st, PRThread **tid)
  96. {
  97. if (tid) *tid = st->tid;
  98. return st->id;
  99. }
  100. static void st_enableTCPnodelay(SearchThread *st)
  101. {
  102. int val = 1;
  103. if (st->soc < 0) {
  104. if (ldap_get_option(st->ld, LDAP_OPT_DESC, (void *)&st->soc)
  105. != LDAP_SUCCESS) {
  106. fprintf(stderr, "T%d: failed on ldap_get_option\n", st->id);
  107. return;
  108. }
  109. }
  110. if (setsockopt(st->soc, IPPROTO_TCP, TCP_NODELAY, (char *)&val,
  111. sizeof(val)))
  112. fprintf(stderr, "T%d: failed in setsockopt 1\n", st->id);
  113. }
  114. /* abruptly disconnect an LDAP connection without unbinding */
  115. static void st_disconnect(SearchThread *st)
  116. {
  117. if (st->soc < 0) {
  118. if (ldap_get_option(st->ld, LDAP_OPT_DESC, (void *)&st->soc)
  119. != LDAP_SUCCESS) {
  120. fprintf(stderr, "T%d: failed on ldap_get_option\n", st->id);
  121. return;
  122. }
  123. }
  124. #ifdef XP_WIN
  125. if (closesocket(st->soc))
  126. fprintf(stderr, "T%d: failed to disconnect\n", st->id);
  127. #else
  128. if (close(st->soc))
  129. fprintf(stderr, "T%d: failed to disconnect\n", st->id);
  130. #endif
  131. st->soc = -1;
  132. }
  133. static int st_bind_core(SearchThread *st, LDAP **ld, char *dn, char *uid)
  134. {
  135. int ret = 0;
  136. int retry = 0;
  137. while (1) {
  138. ret = ldap_simple_bind_s(*ld, dn, uid);
  139. if (LDAP_SUCCESS == ret) {
  140. break;
  141. } else if (LDAP_CONNECT_ERROR == ret && retry < 10) {
  142. retry++;
  143. } else {
  144. fprintf(stderr, "T%d: failed to bind, ldap_simple_bind_s"
  145. "(%s, %s) returned 0x%x (errno %d)\n",
  146. st->id, dn, uid, ret, errno);
  147. *ld = NULL;
  148. return 0;
  149. }
  150. }
  151. return 1;
  152. }
  153. static int st_bind(SearchThread *st)
  154. {
  155. if (!st->ld) {
  156. st->ld = ldap_init(hostname, port);
  157. if (!st->ld) {
  158. fprintf(stderr, "T%d: failed to init\n", st->id);
  159. return 0;
  160. }
  161. }
  162. if (!st->ld2) { /* aux LDAP handle */
  163. st->ld2 = ldap_init(hostname, port);
  164. if (!st->ld2) {
  165. fprintf(stderr, "T%d: failed to init 2\n", st->id);
  166. return 0;
  167. }
  168. if (0 == st_bind_core(st, &(st->ld2), strlen(bindDN) ? bindDN : NULL,
  169. strlen(bindPW) ? bindPW : NULL)) {
  170. return 0;
  171. }
  172. }
  173. if (opType != op_delete && opType != op_modify && opType != op_idxmodify &&
  174. sdattable && sdt_getlen(sdattable) > 0) {
  175. int e;
  176. char *dn, *uid;
  177. do {
  178. e = sdt_getrand(sdattable);
  179. } while (e < 0);
  180. dn = sdt_dn_get(sdattable, e);
  181. uid = sdt_uid_get(sdattable, e);
  182. if (useBFile) {
  183. /* in this test, assuming uid == password */
  184. if (dn) {
  185. if (0 == st_bind_core(st, &(st->ld), dn, uid)) {
  186. return 0;
  187. }
  188. } else if (uid) {
  189. char filterBuffer[100];
  190. char *pFilter;
  191. struct timeval timeout;
  192. int scope = LDAP_SCOPE_SUBTREE, attrsOnly = 0;
  193. LDAPMessage *result;
  194. int retry = 0;
  195. pFilter = filterBuffer;
  196. sprintf(filterBuffer, "(uid=%s)", uid);
  197. timeout.tv_sec = 3600;
  198. timeout.tv_usec = 0;
  199. while (1) {
  200. int ret = ldap_search_st(st->ld2, suffix, scope, pFilter,
  201. NULL, attrsOnly, &timeout, &result);
  202. if (LDAP_SUCCESS == ret) {
  203. break;
  204. } else if ((LDAP_CONNECT_ERROR == ret ||
  205. (LDAP_TIMEOUT == ret)) && retry < 10) {
  206. retry++;
  207. } else {
  208. fprintf(stderr, "T%d: failed to search 1, error=0x%x\n",
  209. st->id, ret);
  210. return 0;
  211. }
  212. }
  213. dn = ldap_get_dn(st->ld2, result);
  214. if (0 == st_bind_core(st, &(st->ld), dn, uid)) {
  215. return 0;
  216. }
  217. } else {
  218. fprintf(stderr, "T%d: no data found, dn: %p, uid: %p\n",
  219. st->id, dn, uid);
  220. return 0;
  221. }
  222. } else {
  223. if (0 == st_bind_core(st, &(st->ld), dn, uid)) {
  224. return 0;
  225. }
  226. }
  227. } else {
  228. if (0 == st_bind_core(st, &(st->ld), strlen(bindDN) ? bindDN : NULL,
  229. strlen(bindPW) ? bindPW : NULL)) {
  230. return 0;
  231. }
  232. }
  233. if (st->soc < 0) {
  234. if (ldap_get_option(st->ld, LDAP_OPT_DESC, (void *)&st->soc)
  235. != LDAP_SUCCESS) {
  236. fprintf(stderr, "T%d: failed on ldap_get_option\n", st->id);
  237. return 0;
  238. }
  239. }
  240. if (setLinger) {
  241. int val;
  242. struct linger l;
  243. val = sizeof(struct linger);
  244. l.l_onoff = 1;
  245. l.l_linger = 0;
  246. if (setsockopt(st->soc, SOL_SOCKET, SO_LINGER, (char *)&l, val) < 0) {
  247. fprintf(stderr, "T%d: failed in setsockopt 2, errno %d (%d)\n",
  248. st->id, errno, (int)st->soc);
  249. st->soc = -1;
  250. return 0;
  251. }
  252. }
  253. return 1;
  254. }
  255. static void st_unbind(SearchThread *st)
  256. {
  257. if (ldap_unbind(st->ld) != LDAP_SUCCESS)
  258. fprintf(stderr, "T%d: failed to unbind\n", st->id);
  259. st->ld = NULL;
  260. st->soc = -1;
  261. }
  262. static int st_search(SearchThread *st)
  263. {
  264. char filterBuffer[100];
  265. char *pFilter;
  266. struct timeval timeout;
  267. struct timeval *timeoutp;
  268. int scope, attrsOnly = 0;
  269. LDAPMessage *result;
  270. int ret;
  271. scope = myScope;
  272. if (ntable || numeric) {
  273. char *s = NULL;
  274. char num[8];
  275. if (! numeric) {
  276. do {
  277. s = nt_getrand(ntable);
  278. } while ((s) && (strlen(s) < 1));
  279. } else {
  280. sprintf(num, "%d", get_large_random_number() % numeric);
  281. s = num;
  282. }
  283. sprintf(filterBuffer, filter, s);
  284. pFilter = filterBuffer;
  285. } else {
  286. pFilter = filter;
  287. }
  288. /* Try to get attributes from the attrNameTable */
  289. if (!attrToReturn)
  290. attrToReturn = nt_get_all(attrTable);
  291. if (searchTimelimit <= 0) {
  292. timeoutp = NULL;
  293. } else {
  294. timeout.tv_sec = searchTimelimit;
  295. timeout.tv_usec = 0;
  296. timeoutp = &timeout;
  297. }
  298. ret = ldap_search_st(st->ld, suffix, scope, pFilter, attrToReturn,
  299. attrsOnly, timeoutp, &result);
  300. if (ret != LDAP_SUCCESS) {
  301. fprintf(stderr, "T%d: failed to search 2, error=0x%02X\n",
  302. st->id, ret);
  303. }
  304. ldap_msgfree(result);
  305. return ret;
  306. }
  307. static void st_make_random_tel_number(char *pstr)
  308. {
  309. static char *area_codes[] = {"303", "415", "408", "650", "216", "580", 0};
  310. int idx = rand() % 6;
  311. sprintf(pstr, "+1 %s %03d %04d",
  312. area_codes[idx], rand() % 1000, rand() % 10000);
  313. }
  314. static int st_modify_nonidx(SearchThread *st)
  315. {
  316. LDAPMod *attrs[2];
  317. LDAPMod attr_description;
  318. int e;
  319. int rval;
  320. char *dn = NULL;
  321. char description[256];
  322. char *description_values[2];
  323. /* Decide what entry to modify, for this we need a table */
  324. if (NULL == sdattable || sdt_getlen(sdattable) == 0) {
  325. fprintf(stderr, "-m option requires a DN file. Use -B file.\n");
  326. return 0;
  327. }
  328. /* Get the target dn */
  329. do {
  330. e = sdt_getrand(sdattable);
  331. } while (e < 0);
  332. dn = sdt_dn_get(sdattable, e);
  333. sprintf(description, "%s modified at %lu", dn, time(NULL));
  334. description_values[0] = description;
  335. description_values[1] = NULL;
  336. attrs[0] = &attr_description;
  337. attrs[1] = NULL;
  338. attr_description.mod_op = LDAP_MOD_REPLACE;
  339. attr_description.mod_type = "description";
  340. attr_description.mod_values = description_values;
  341. rval = ldap_modify_s(st->ld, dn, attrs);
  342. if (rval != LDAP_SUCCESS) {
  343. fprintf(stderr, "T%d: Failed to modify error=0x%x\n", st->id, rval);
  344. fprintf(stderr, "dn: %s\n", dn);
  345. }
  346. return rval;
  347. }
  348. static int st_modify_idx(SearchThread *st)
  349. {
  350. LDAPMod *attrs[2];
  351. LDAPMod attr_telephonenumber;
  352. int e;
  353. int rval;
  354. char *dn = NULL;
  355. char telno[32];
  356. char *telephonenumber_values[2];
  357. /* Decide what entry to modify, for this we need a table */
  358. if (NULL == sdattable || sdt_getlen(sdattable) == 0) {
  359. fprintf(stderr, "-m option requires a DN file. Use -B file.\n");
  360. return 0;
  361. }
  362. /* Get the target dn */
  363. do {
  364. e = sdt_getrand(sdattable);
  365. } while (e < 0);
  366. dn = sdt_dn_get(sdattable, e);
  367. /* Make new mod values */
  368. st_make_random_tel_number(telno);
  369. telephonenumber_values[0] = telno;
  370. telephonenumber_values[1] = NULL;
  371. attrs[0] = &attr_telephonenumber;
  372. attrs[1] = NULL;
  373. attr_telephonenumber.mod_op = LDAP_MOD_REPLACE;
  374. attr_telephonenumber.mod_type = "telephonenumber";
  375. attr_telephonenumber.mod_values = telephonenumber_values;
  376. rval = ldap_modify_s(st->ld, dn, attrs);
  377. if (rval != LDAP_SUCCESS) {
  378. fprintf(stderr, "T%d: Failed to modify error=0x%x\n", st->id, rval);
  379. fprintf(stderr, "dn: %s\n", dn);
  380. }
  381. return rval;
  382. }
  383. static int st_compare(SearchThread *st)
  384. {
  385. int rval;
  386. int compare_true;
  387. int correct_answer;
  388. int e;
  389. char *dn = NULL;
  390. char *uid = NULL;
  391. char uid0[100];
  392. /* Decide what entry to modify, for this we need a table */
  393. if (NULL == sdattable || sdt_getlen(sdattable) == 0) {
  394. fprintf(stderr, "-c option requires a DN file. Use -B file.\n");
  395. return 0;
  396. }
  397. /* Get the target dn */
  398. do {
  399. e = sdt_getrand(sdattable);
  400. } while (e < 0);
  401. dn = sdt_dn_get(sdattable, e);
  402. uid = sdt_uid_get(sdattable, e);
  403. compare_true = ( (rand() % 5) < 2 );
  404. if (!compare_true) {
  405. strcpy(uid0, uid);
  406. uid0[0] = '@'; /* make it not matched */
  407. uid = uid0;
  408. }
  409. rval = ldap_compare_s(st->ld, dn, "uid", uid);
  410. correct_answer = compare_true ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
  411. if (rval == correct_answer) {
  412. rval = LDAP_SUCCESS;
  413. } else {
  414. fprintf(stderr, "T%d: Failed to compare error=0x%x (%d)\n",
  415. st->id, rval, correct_answer);
  416. fprintf(stderr, "dn: %s, uid: %s\n", dn, uid);
  417. }
  418. return rval;
  419. }
  420. static int st_delete(SearchThread *st)
  421. {
  422. char *dn = NULL;
  423. int rval;
  424. int e;
  425. /* Decide what entry to modify, for this we need a table */
  426. if (NULL == sdattable || sdt_getlen(sdattable) == 0) {
  427. fprintf(stderr, "-d option requires a DN file. Use -B file.\n");
  428. return 0;
  429. }
  430. /* Get the target dn */
  431. do {
  432. e = sdt_getrand(sdattable);
  433. } while (e < 0);
  434. dn = sdt_dn_get(sdattable, e);
  435. rval = ldap_delete_s(st->ld, dn);
  436. if (rval != LDAP_SUCCESS) {
  437. if (rval == LDAP_NO_SUCH_OBJECT) {
  438. rval = LDAP_SUCCESS;
  439. } else {
  440. fprintf(stderr, "T%d: Failed to delete error=0x%x\n", st->id, rval);
  441. fprintf(stderr, "dn: %s\n", dn);
  442. }
  443. }
  444. return rval;
  445. }
  446. /* the main thread */
  447. void search_start(void *v)
  448. {
  449. SearchThread *st = (SearchThread *)v;
  450. PRIntervalTime timer;
  451. int notBound = 1, res = LDAP_SUCCESS, searches = 0;
  452. PRUint32 span;
  453. st->alive = 1;
  454. st->ld = 0;
  455. while (1) {
  456. timer = PR_IntervalNow();
  457. /* bind if we need to */
  458. if (doBind || notBound) {
  459. res = st_bind(st);
  460. if (noDelay)
  461. st_enableTCPnodelay(st);
  462. if (!res) {
  463. st_unbind(st);
  464. continue; /* error */
  465. }
  466. notBound = 0;
  467. }
  468. /* do the operation */
  469. if (!noOp) {
  470. switch(opType) {
  471. case op_modify:
  472. res = st_modify_nonidx(st);
  473. break;
  474. case op_idxmodify:
  475. res = st_modify_idx(st);
  476. break;
  477. case op_search:
  478. res = st_search(st);
  479. break;
  480. case op_compare:
  481. res = st_compare(st);
  482. break;
  483. case op_delete:
  484. res = st_delete(st);
  485. break;
  486. default:
  487. fprintf(stderr, "Illegal operation type specified.\n");
  488. return;
  489. }
  490. }
  491. if (LDAP_SUCCESS == res) {
  492. st->retry = 0;
  493. } else if (LDAP_CONNECT_ERROR == res && st->retry < 10) {
  494. st->retry++;
  495. } else {
  496. break; /* error */
  497. }
  498. if (doBind) {
  499. if (noUnBind)
  500. st_disconnect(st);
  501. st_unbind(st);
  502. } else if (reconnect) {
  503. searches++;
  504. if (searches >= reconnect) {
  505. /* unceremoniously disconnect, reconnect next cycle */
  506. st_disconnect(st);
  507. st_unbind(st);
  508. notBound = 1;
  509. searches = 0;
  510. }
  511. }
  512. span = PR_IntervalToMilliseconds(PR_IntervalNow()-timer);
  513. /* update data */
  514. PR_Lock(st->lock);
  515. if (0 == st->retry) { /* only when succeeded */
  516. st->searchCount++;
  517. if (st->mintime > span)
  518. st->mintime = span;
  519. if (st->maxtime < span)
  520. st->maxtime = span;
  521. }
  522. st->alive = 1;
  523. PR_Unlock(st->lock);
  524. }
  525. }
  526. /* fetches the current min/max times and the search count, and clears them */
  527. void st_getCountMinMax(SearchThread *st, PRUint32 *count, PRUint32 *min,
  528. PRUint32 *max)
  529. {
  530. PR_Lock(st->lock);
  531. if (count) {
  532. *count = st->searchCount;
  533. st->searchCount = 0;
  534. }
  535. if (min) {
  536. *min = st->mintime;
  537. st->mintime = 10000;
  538. }
  539. if (max) {
  540. *max = st->maxtime;
  541. st->maxtime = 0;
  542. }
  543. st->alive--;
  544. PR_Unlock(st->lock);
  545. }
  546. int st_alive(SearchThread *st)
  547. {
  548. int alive;
  549. PR_Lock(st->lock);
  550. alive = st->alive;
  551. PR_Unlock(st->lock);
  552. return alive;
  553. }