db_server_util.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:14k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /*-
  2.  * See the file LICENSE for redistribution information.
  3.  *
  4.  * Copyright (c) 2000
  5.  *      Sleepycat Software.  All rights reserved.
  6.  */
  7. #include "db_config.h"
  8. #ifndef lint
  9. static const char revid[] = "$Id: db_server_util.c,v 1.32 2001/01/18 18:36:59 bostic Exp $";
  10. #endif /* not lint */
  11. #ifndef NO_SYSTEM_INCLUDES
  12. #include <sys/types.h>
  13. #if TIME_WITH_SYS_TIME
  14. #include <sys/time.h>
  15. #include <time.h>
  16. #else
  17. #if HAVE_SYS_TIME_H
  18. #include <sys/time.h>
  19. #else
  20. #include <time.h>
  21. #endif
  22. #endif
  23. #include <rpc/rpc.h>
  24. #include <limits.h>
  25. #include <signal.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include <unistd.h>
  30. #endif
  31. #include "db_server.h"
  32. #include "db_int.h"
  33. #include "clib_ext.h"
  34. #include "db_server_int.h"
  35. #include "rpc_server_ext.h"
  36. #include "common_ext.h"
  37. extern int __dbsrv_main  __P((void));
  38. static int add_home __P((char *));
  39. static int env_recover __P((char *));
  40. static void __dbclear_child __P((ct_entry *));
  41. static LIST_HEAD(cthead, ct_entry) __dbsrv_head;
  42. static LIST_HEAD(homehead, home_entry) __dbsrv_home;
  43. static long __dbsrv_defto = DB_SERVER_TIMEOUT;
  44. static long __dbsrv_maxto = DB_SERVER_MAXTIMEOUT;
  45. static long __dbsrv_idleto = DB_SERVER_IDLETIMEOUT;
  46. static char *logfile = NULL;
  47. static char *prog;
  48. static void usage __P((char *));
  49. static void version_check __P((void));
  50. int __dbsrv_verbose = 0;
  51. int
  52. main(argc, argv)
  53. int argc;
  54. char **argv;
  55. {
  56. extern char *optarg;
  57. extern int optind;
  58. CLIENT *cl;
  59. int ch, ret;
  60. prog = argv[0];
  61. version_check();
  62. /*
  63.  * Check whether another server is running or not.  There
  64.  * is a race condition where two servers could be racing to
  65.  * register with the portmapper.  The goal of this check is to
  66.  * forbid running additional servers (like those started from
  67.  * the test suite) if the user is already running one.
  68.  *
  69.  * XXX
  70.  * This does not solve nor prevent two servers from being
  71.  * started at the same time and running recovery at the same
  72.  * time on the same environments.
  73.  */
  74. if ((cl = clnt_create("localhost",
  75.     DB_SERVERPROG, DB_SERVERVERS, "tcp")) != NULL) {
  76. fprintf(stderr,
  77.     "%s: Berkeley DB RPC server already running.n", prog);
  78. clnt_destroy(cl);
  79. exit(1);
  80. }
  81. LIST_INIT(&__dbsrv_home);
  82. while ((ch = getopt(argc, argv, "h:I:L:t:T:Vv")) != EOF)
  83. switch (ch) {
  84. case 'h':
  85. (void)add_home(optarg);
  86. break;
  87. case 'I':
  88. (void)__db_getlong(NULL, prog, optarg, 1,
  89.     LONG_MAX, &__dbsrv_idleto);
  90. break;
  91. case 'L':
  92. logfile = optarg;
  93. break;
  94. case 't':
  95. (void)__db_getlong(NULL, prog, optarg, 1,
  96.     LONG_MAX, &__dbsrv_defto);
  97. break;
  98. case 'T':
  99. (void)__db_getlong(NULL, prog, optarg, 1,
  100.     LONG_MAX, &__dbsrv_maxto);
  101. break;
  102. case 'V':
  103. printf("%sn", db_version(NULL, NULL, NULL));
  104. exit(0);
  105. case 'v':
  106. __dbsrv_verbose = 1;
  107. break;
  108. default:
  109. usage(prog);
  110. }
  111. /*
  112.  * Check default timeout against maximum timeout
  113.  */
  114. if (__dbsrv_defto > __dbsrv_maxto)
  115. __dbsrv_defto = __dbsrv_maxto;
  116. /*
  117.  * Check default timeout against idle timeout
  118.  * It would be bad to timeout environments sooner than txns.
  119.  */
  120. if (__dbsrv_defto > __dbsrv_idleto)
  121. printf("%s:  WARNING: Idle timeout %ld is less than resource timeout %ldn",
  122.     prog, __dbsrv_idleto, __dbsrv_defto);
  123. LIST_INIT(&__dbsrv_head);
  124. /*
  125.  * If a client crashes during an RPC, our reply to it
  126.  * generates a SIGPIPE.  Ignore SIGPIPE so we don't exit unnecessarily.
  127.  */
  128. #ifdef SIGPIPE
  129. signal(SIGPIPE, SIG_IGN);
  130. #endif
  131. if (logfile != NULL && __db_util_logset("berkeley_db_svc", logfile))
  132. exit(1);
  133. /*
  134.  * Now that we are ready to start, run recovery on all the
  135.  * environments specified.
  136.  */
  137. if ((ret = env_recover(prog)) != 0)
  138. exit(1);
  139. /*
  140.  * We've done our setup, now call the generated server loop
  141.  */
  142. if (__dbsrv_verbose)
  143. printf("%s:  Ready to receive requestsn", prog);
  144. __dbsrv_main();
  145. /* NOTREACHED */
  146. abort();
  147. }
  148. static void
  149. usage(prog)
  150. char *prog;
  151. {
  152. fprintf(stderr, "usage: %s %snt%sn", prog,
  153.     "[-Vv] [-h home]",
  154.     "[-I idletimeout] [-L logfile] [-t def_timeout] [-T maxtimeout]");
  155. exit(1);
  156. }
  157. static void
  158. version_check()
  159. {
  160. int v_major, v_minor, v_patch;
  161. /* Make sure we're loaded with the right version of the DB library. */
  162. (void)db_version(&v_major, &v_minor, &v_patch);
  163. if (v_major != DB_VERSION_MAJOR ||
  164.     v_minor != DB_VERSION_MINOR || v_patch != DB_VERSION_PATCH) {
  165. fprintf(stderr,
  166. "%s: version %d.%d.%d doesn't match library version %d.%d.%dn",
  167.     prog, DB_VERSION_MAJOR, DB_VERSION_MINOR,
  168.     DB_VERSION_PATCH, v_major, v_minor, v_patch);
  169. exit (1);
  170. }
  171. }
  172. /*
  173.  * PUBLIC: void __dbsrv_settimeout __P((ct_entry *, u_int32_t));
  174.  */
  175. void
  176. __dbsrv_settimeout(ctp, to)
  177. ct_entry *ctp;
  178. u_int32_t to;
  179. {
  180. if (to > (u_int32_t)__dbsrv_maxto)
  181. ctp->ct_timeout = __dbsrv_maxto;
  182. else if (to <= 0)
  183. ctp->ct_timeout = __dbsrv_defto;
  184. else
  185. ctp->ct_timeout = to;
  186. }
  187. /*
  188.  * PUBLIC: void __dbsrv_timeout __P((int));
  189.  */
  190. void
  191. __dbsrv_timeout(force)
  192. int force;
  193. {
  194. static long to_hint = -1;
  195. DBC *dbcp;
  196. time_t t;
  197. long to;
  198. ct_entry *ctp, *nextctp;
  199. if ((t = time(NULL)) == -1)
  200. return;
  201. /*
  202.  * Check hint.  If hint is further in the future
  203.  * than now, no work to do.
  204.  */
  205. if (!force && to_hint > 0 && t < to_hint)
  206. return;
  207. to_hint = -1;
  208. /*
  209.  * Timeout transactions or cursors holding DB resources.
  210.  * Do this before timing out envs to properly release resources.
  211.  *
  212.  * !!!
  213.  * We can just loop through this list looking for cursors and txns.
  214.  * We do not need to verify txn and cursor relationships at this
  215.  * point because we maintain the list in LIFO order *and* we
  216.  * maintain activity in the ultimate txn parent of any cursor
  217.  * so either everything in a txn is timing out, or nothing.
  218.  * So, since we are LIFO, we will correctly close/abort all the
  219.  * appropriate handles, in the correct order.
  220.  */
  221. for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; ctp = nextctp) {
  222. nextctp = LIST_NEXT(ctp, entries);
  223. switch (ctp->ct_type) {
  224. case CT_TXN:
  225. to = *(ctp->ct_activep) + ctp->ct_timeout;
  226. /* TIMEOUT */
  227. if (to < t) {
  228. if (__dbsrv_verbose)
  229. printf("Timing out txn id %ldn",
  230.     ctp->ct_id);
  231. (void)txn_abort((DB_TXN *)ctp->ct_anyp);
  232. __dbdel_ctp(ctp);
  233. /*
  234.  * If we timed out an txn, we may have closed
  235.  * all sorts of ctp's.
  236.  * So start over with a guaranteed good ctp.
  237.  */
  238. nextctp = LIST_FIRST(&__dbsrv_head);
  239. } else if ((to_hint > 0 && to_hint > to) ||
  240.     to_hint == -1)
  241. to_hint = to;
  242. break;
  243. case CT_CURSOR:
  244. case (CT_JOINCUR | CT_CURSOR):
  245. to = *(ctp->ct_activep) + ctp->ct_timeout;
  246. /* TIMEOUT */
  247. if (to < t) {
  248. if (__dbsrv_verbose)
  249. printf("Timing out cursor %ldn",
  250.     ctp->ct_id);
  251. dbcp = (DBC *)ctp->ct_anyp;
  252. (void)__dbc_close_int(ctp);
  253. /*
  254.  * Start over with a guaranteed good ctp.
  255.  */
  256. nextctp = LIST_FIRST(&__dbsrv_head);
  257. } else if ((to_hint > 0 && to_hint > to) ||
  258.     to_hint == -1)
  259. to_hint = to;
  260. break;
  261. default:
  262. break;
  263. }
  264. }
  265. /*
  266.  * Timeout idle handles.
  267.  * If we are forcing a timeout, we'll close all env handles.
  268.  */
  269. for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; ctp = nextctp) {
  270. nextctp = LIST_NEXT(ctp, entries);
  271. if (ctp->ct_type != CT_ENV)
  272. continue;
  273. to = *(ctp->ct_activep) + ctp->ct_idle;
  274. /* TIMEOUT */
  275. if (to < t || force) {
  276. if (__dbsrv_verbose)
  277. printf("Timing out env id %ldn", ctp->ct_id);
  278. (void)__dbenv_close_int(ctp->ct_id, 0);
  279. /*
  280.  * If we timed out an env, we may have closed
  281.  * all sorts of ctp's (maybe even all of them.
  282.  * So start over with a guaranteed good ctp.
  283.  */
  284. nextctp = LIST_FIRST(&__dbsrv_head);
  285. }
  286. }
  287. }
  288. /*
  289.  * RECURSIVE FUNCTION.  We need to clear/free any number of levels of nested
  290.  * layers.
  291.  */
  292. static void
  293. __dbclear_child(parent)
  294. ct_entry *parent;
  295. {
  296. ct_entry *ctp, *nextctp;
  297. for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL;
  298.     ctp = nextctp) {
  299. nextctp = LIST_NEXT(ctp, entries);
  300. if (ctp->ct_type == 0)
  301. continue;
  302. if (ctp->ct_parent == parent) {
  303. __dbclear_child(ctp);
  304. /*
  305.  * Need to do this here because le_next may
  306.  * have changed with the recursive call and we
  307.  * don't want to point to a removed entry.
  308.  */
  309. nextctp = LIST_NEXT(ctp, entries);
  310. __dbclear_ctp(ctp);
  311. }
  312. }
  313. }
  314. /*
  315.  * PUBLIC: void __dbclear_ctp __P((ct_entry *));
  316.  */
  317. void
  318. __dbclear_ctp(ctp)
  319. ct_entry *ctp;
  320. {
  321. LIST_REMOVE(ctp, entries);
  322. __os_free(ctp, sizeof(ct_entry));
  323. }
  324. /*
  325.  * PUBLIC: void __dbdel_ctp __P((ct_entry *));
  326.  */
  327. void
  328. __dbdel_ctp(parent)
  329. ct_entry *parent;
  330. {
  331. __dbclear_child(parent);
  332. __dbclear_ctp(parent);
  333. }
  334. /*
  335.  * PUBLIC: ct_entry *new_ct_ent __P((u_int32_t *));
  336.  */
  337. ct_entry *
  338. new_ct_ent(errp)
  339. u_int32_t *errp;
  340. {
  341. time_t t;
  342. ct_entry *ctp, *octp;
  343. int ret;
  344. if ((ret = __os_malloc(NULL, sizeof(ct_entry), NULL, &ctp)) != 0) {
  345. *errp = ret;
  346. return (NULL);
  347. }
  348. /*
  349.  * Get the time as ID.  We may service more than one request per
  350.  * second however.  If we are, then increment id value until we
  351.  * find an unused one.  We insert entries in LRU fashion at the
  352.  * head of the list.  So, if the first entry doesn't match, then
  353.  * we know for certain that we can use our entry.
  354.  */
  355. if ((t = time(NULL)) == -1) {
  356. *errp = t;
  357. __os_free(ctp, sizeof(ct_entry));
  358. return (NULL);
  359. }
  360. octp = LIST_FIRST(&__dbsrv_head);
  361. if (octp != NULL && octp->ct_id >= t)
  362. t = octp->ct_id + 1;
  363. ctp->ct_id = t;
  364. ctp->ct_idle = __dbsrv_idleto;
  365. ctp->ct_activep = &ctp->ct_active;
  366. ctp->ct_origp = NULL;
  367. LIST_INSERT_HEAD(&__dbsrv_head, ctp, entries);
  368. return (ctp);
  369. }
  370. /*
  371.  * PUBLIC: ct_entry *get_tableent __P((long));
  372.  */
  373. ct_entry *
  374. get_tableent(id)
  375. long id;
  376. {
  377. ct_entry *ctp;
  378. for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL;
  379.     ctp = LIST_NEXT(ctp, entries))
  380. if (ctp->ct_id == id)
  381. return (ctp);
  382. return (NULL);
  383. }
  384. /*
  385.  * PUBLIC: void __dbsrv_active __P((ct_entry *));
  386.  */
  387. void
  388. __dbsrv_active(ctp)
  389. ct_entry *ctp;
  390. {
  391. time_t t;
  392. ct_entry *envctp;
  393. if (ctp == NULL)
  394. return;
  395. if ((t = time(NULL)) == -1)
  396. return;
  397. *(ctp->ct_activep) = t;
  398. if ((envctp = ctp->ct_envparent) == NULL)
  399. return;
  400. *(envctp->ct_activep) = t;
  401. return;
  402. }
  403. /*
  404.  * PUBLIC: int __dbc_close_int __P((ct_entry *));
  405.  */
  406. int
  407. __dbc_close_int(dbc_ctp)
  408. ct_entry *dbc_ctp;
  409. {
  410. DBC *dbc;
  411. int ret;
  412. ct_entry *ctp;
  413. dbc = (DBC *)dbc_ctp->ct_anyp;
  414. ret = dbc->c_close(dbc);
  415. /*
  416.  * If this cursor is a join cursor then we need to fix up the
  417.  * cursors that it was joined from so that they are independent again.
  418.  */
  419. if (dbc_ctp->ct_type & CT_JOINCUR)
  420. for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL;
  421.     ctp = LIST_NEXT(ctp, entries)) {
  422. /*
  423.  * Test if it is a join cursor, and if it is part
  424.  * of this one.
  425.  */
  426. if ((ctp->ct_type & CT_JOIN) &&
  427.     ctp->ct_activep == &dbc_ctp->ct_active) {
  428. ctp->ct_type &= ~CT_JOIN;
  429. ctp->ct_activep = ctp->ct_origp;
  430. __dbsrv_active(ctp);
  431. }
  432. }
  433. __dbclear_ctp(dbc_ctp);
  434. return (ret);
  435. }
  436. /*
  437.  * PUBLIC: int __dbenv_close_int __P((long, int));
  438.  */
  439. int
  440. __dbenv_close_int(id, flags)
  441. long id;
  442. int flags;
  443. {
  444. DB_ENV *dbenv;
  445. int ret;
  446. ct_entry *ctp;
  447. ctp = get_tableent(id);
  448. if (ctp == NULL)
  449. return (DB_NOSERVER_ID);
  450. DB_ASSERT(ctp->ct_type == CT_ENV);
  451. dbenv = ctp->ct_envp;
  452. ret = dbenv->close(dbenv, flags);
  453. __dbdel_ctp(ctp);
  454. return (ret);
  455. }
  456. static int
  457. add_home(home)
  458. char *home;
  459. {
  460. home_entry *hp, *homep;
  461. int ret;
  462. if ((ret = __os_malloc(NULL, sizeof(home_entry), NULL, &hp)) != 0)
  463. return (ret);
  464. if ((ret = __os_malloc(NULL, strlen(home)+1, NULL, &hp->home)) != 0)
  465. return (ret);
  466. memcpy(hp->home, home, strlen(home)+1);
  467. hp->dir = home;
  468. /*
  469.  * This loop is to remove any trailing path separators,
  470.  * to assure hp->name points to the last component.
  471.  */
  472. hp->name = __db_rpath(home);
  473. *(hp->name) = '';
  474. hp->name++;
  475. while (*(hp->name) == '') {
  476. hp->name = __db_rpath(home);
  477. *(hp->name) = '';
  478. hp->name++;
  479. }
  480. /*
  481.  * Now we have successfully added it.  Make sure there are no
  482.  * identical names.
  483.  */
  484. for (homep = LIST_FIRST(&__dbsrv_home); homep != NULL;
  485.     homep = LIST_NEXT(homep, entries))
  486. if (strcmp(homep->name, hp->name) == 0) {
  487. printf("Already added home name %s, at directory %sn",
  488.     hp->name, homep->dir);
  489. return (-1);
  490. }
  491. LIST_INSERT_HEAD(&__dbsrv_home, hp, entries);
  492. if (__dbsrv_verbose)
  493. printf("Added home %s in dir %sn", hp->name, hp->dir);
  494. return (0);
  495. }
  496. /*
  497.  * PUBLIC: char *get_home __P((char *));
  498.  */
  499. char *
  500. get_home(name)
  501. char *name;
  502. {
  503. home_entry *hp;
  504. for (hp = LIST_FIRST(&__dbsrv_home); hp != NULL;
  505.     hp = LIST_NEXT(hp, entries))
  506. if (strcmp(name, hp->name) == 0)
  507. return (hp->home);
  508. return (NULL);
  509. }
  510. static int
  511. env_recover(progname)
  512. char *progname;
  513. {
  514. DB_ENV *dbenv;
  515. home_entry *hp;
  516. u_int32_t flags;
  517. int exitval, ret;
  518. for (hp = LIST_FIRST(&__dbsrv_home); hp != NULL;
  519.     hp = LIST_NEXT(hp, entries)) {
  520. exitval = 0;
  521. if ((ret = db_env_create(&dbenv, 0)) != 0) {
  522. fprintf(stderr, "%s: db_env_create: %sn",
  523.     progname, db_strerror(ret));
  524. exit(1);
  525. }
  526. if (__dbsrv_verbose == 1) {
  527. (void)dbenv->set_verbose(dbenv, DB_VERB_RECOVERY, 1);
  528. (void)dbenv->set_verbose(dbenv, DB_VERB_CHKPOINT, 1);
  529. }
  530. dbenv->set_errfile(dbenv, stderr);
  531. dbenv->set_errpfx(dbenv, progname);
  532. /*
  533.  * Initialize the env with DB_RECOVER.  That is all we
  534.  * have to do to run recovery.
  535.  */
  536. if (__dbsrv_verbose)
  537. printf("Running recovery on %sn", hp->home);
  538. flags = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
  539.     DB_INIT_TXN | DB_PRIVATE | DB_USE_ENVIRON | DB_RECOVER;
  540. if ((ret = dbenv->open(dbenv, hp->home, flags, 0)) != 0) {
  541. dbenv->err(dbenv, ret, "DBENV->open");
  542. goto error;
  543. }
  544. if (0) {
  545. error: exitval = 1;
  546. }
  547. if ((ret = dbenv->close(dbenv, 0)) != 0) {
  548. exitval = 1;
  549. fprintf(stderr, "%s: dbenv->close: %sn",
  550.     progname, db_strerror(ret));
  551. }
  552. if (exitval)
  553. return (exitval);
  554. }
  555. return (0);
  556. }