00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "db_config.h"
00011
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014
00015 #if TIME_WITH_SYS_TIME
00016 #include <sys/time.h>
00017 #include <time.h>
00018 #else
00019 #if HAVE_SYS_TIME_H
00020 #include <sys/time.h>
00021 #else
00022 #include <time.h>
00023 #endif
00024 #endif
00025
00026 #include <rpc/rpc.h>
00027
00028 #include <limits.h>
00029 #include <signal.h>
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 #include <unistd.h>
00034 #endif
00035
00036 #include "db_server.h"
00037
00038 #include "db_int.h"
00039 #include "dbinc_auto/clib_ext.h"
00040 #include "dbinc/db_server_int.h"
00041 #include "dbinc_auto/common_ext.h"
00042 #include "dbinc_auto/rpc_server_ext.h"
00043
00044 static int add_home __P((char *));
00045 static int add_passwd __P((char *));
00046 static int env_recover __P((char *));
00047 static void __dbclear_child __P((ct_entry *));
00048
00049 static LIST_HEAD(cthead, ct_entry) __dbsrv_head;
00050 static LIST_HEAD(homehead, home_entry) __dbsrv_home;
00051 static long __dbsrv_defto = DB_SERVER_TIMEOUT;
00052 static long __dbsrv_maxto = DB_SERVER_MAXTIMEOUT;
00053 static long __dbsrv_idleto = DB_SERVER_IDLETIMEOUT;
00054 static char *logfile = NULL;
00055 static char *prog;
00056
00057 static void usage __P((void));
00058 static void version_check __P((void));
00059
00060 int __dbsrv_verbose = 0;
00061
00062 int
00063 main(argc, argv)
00064 int argc;
00065 char **argv;
00066 {
00067 extern int __dbsrv_main();
00068 extern char *optarg;
00069 CLIENT *cl;
00070 int ch, ret;
00071 char *passwd;
00072
00073 prog = argv[0];
00074
00075 version_check();
00076
00077 ret = 0;
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090 if ((cl = clnt_create("localhost",
00091 DB_RPC_SERVERPROG, DB_RPC_SERVERVERS, "tcp")) != NULL) {
00092 fprintf(stderr,
00093 "%s: Berkeley DB RPC server already running.\n", prog);
00094 clnt_destroy(cl);
00095 return (EXIT_FAILURE);
00096 }
00097
00098 LIST_INIT(&__dbsrv_home);
00099 while ((ch = getopt(argc, argv, "h:I:L:P:t:T:Vv")) != EOF)
00100 switch (ch) {
00101 case 'h':
00102 (void)add_home(optarg);
00103 break;
00104 case 'I':
00105 if (__db_getlong(NULL, prog,
00106 optarg, 1, LONG_MAX, &__dbsrv_idleto))
00107 return (EXIT_FAILURE);
00108 break;
00109 case 'L':
00110 logfile = optarg;
00111 break;
00112 case 'P':
00113 passwd = strdup(optarg);
00114 memset(optarg, 0, strlen(optarg));
00115 if (passwd == NULL) {
00116 fprintf(stderr, "%s: strdup: %s\n",
00117 prog, strerror(errno));
00118 return (EXIT_FAILURE);
00119 }
00120 if ((ret = add_passwd(passwd)) != 0) {
00121 fprintf(stderr, "%s: strdup: %s\n",
00122 prog, strerror(ret));
00123 return (EXIT_FAILURE);
00124 }
00125 break;
00126 case 't':
00127 if (__db_getlong(NULL, prog,
00128 optarg, 1, LONG_MAX, &__dbsrv_defto))
00129 return (EXIT_FAILURE);
00130 break;
00131 case 'T':
00132 if (__db_getlong(NULL, prog,
00133 optarg, 1, LONG_MAX, &__dbsrv_maxto))
00134 return (EXIT_FAILURE);
00135 break;
00136 case 'V':
00137 printf("%s\n", db_version(NULL, NULL, NULL));
00138 return (EXIT_SUCCESS);
00139 case 'v':
00140 __dbsrv_verbose = 1;
00141 break;
00142 default:
00143 usage();
00144 }
00145
00146
00147
00148 if (__dbsrv_defto > __dbsrv_maxto)
00149 __dbsrv_defto = __dbsrv_maxto;
00150
00151
00152
00153
00154
00155 if (__dbsrv_defto > __dbsrv_idleto)
00156 fprintf(stderr,
00157 "%s: WARNING: Idle timeout %ld is less than resource timeout %ld\n",
00158 prog, __dbsrv_idleto, __dbsrv_defto);
00159
00160 LIST_INIT(&__dbsrv_head);
00161
00162
00163
00164
00165
00166 #ifdef SIGPIPE
00167 signal(SIGPIPE, SIG_IGN);
00168 #endif
00169
00170 if (logfile != NULL && __db_util_logset("berkeley_db_svc", logfile))
00171 return (EXIT_FAILURE);
00172
00173
00174
00175
00176
00177 if (env_recover(prog) != 0)
00178 return (EXIT_FAILURE);
00179
00180
00181
00182
00183 if (__dbsrv_verbose)
00184 printf("%s: Ready to receive requests\n", prog);
00185 __dbsrv_main();
00186
00187 abort();
00188
00189
00190 return (0);
00191 }
00192
00193 static void
00194 usage()
00195 {
00196 fprintf(stderr, "usage: %s %s\n\t%s\n", prog,
00197 "[-Vv] [-h home] [-P passwd]",
00198 "[-I idletimeout] [-L logfile] [-t def_timeout] [-T maxtimeout]");
00199 exit(EXIT_FAILURE);
00200 }
00201
00202 static void
00203 version_check()
00204 {
00205 int v_major, v_minor, v_patch;
00206
00207
00208 (void)db_version(&v_major, &v_minor, &v_patch);
00209 if (v_major != DB_VERSION_MAJOR ||
00210 v_minor != DB_VERSION_MINOR || v_patch != DB_VERSION_PATCH) {
00211 fprintf(stderr,
00212 "%s: version %d.%d.%d doesn't match library version %d.%d.%d\n",
00213 prog, DB_VERSION_MAJOR, DB_VERSION_MINOR,
00214 DB_VERSION_PATCH, v_major, v_minor, v_patch);
00215 exit(EXIT_FAILURE);
00216 }
00217 }
00218
00219
00220
00221
00222 void
00223 __dbsrv_settimeout(ctp, to)
00224 ct_entry *ctp;
00225 u_int32_t to;
00226 {
00227 if (to > (u_int32_t)__dbsrv_maxto)
00228 ctp->ct_timeout = __dbsrv_maxto;
00229 else if (to <= 0)
00230 ctp->ct_timeout = __dbsrv_defto;
00231 else
00232 ctp->ct_timeout = to;
00233 }
00234
00235
00236
00237
00238 void
00239 __dbsrv_timeout(force)
00240 int force;
00241 {
00242 static long to_hint = -1;
00243 time_t t;
00244 long to;
00245 ct_entry *ctp, *nextctp;
00246
00247 if ((t = time(NULL)) == -1)
00248 return;
00249
00250
00251
00252
00253
00254 if (!force && to_hint > 0 && t < to_hint)
00255 return;
00256 to_hint = -1;
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; ctp = nextctp) {
00271 nextctp = LIST_NEXT(ctp, entries);
00272 switch (ctp->ct_type) {
00273 case CT_TXN:
00274 to = *(ctp->ct_activep) + ctp->ct_timeout;
00275
00276 if (to < t) {
00277 if (__dbsrv_verbose)
00278 printf("Timing out txn id %ld\n",
00279 ctp->ct_id);
00280 (void)((DB_TXN *)ctp->ct_anyp)->
00281 abort((DB_TXN *)ctp->ct_anyp);
00282 __dbdel_ctp(ctp);
00283
00284
00285
00286
00287
00288 nextctp = LIST_FIRST(&__dbsrv_head);
00289 } else if ((to_hint > 0 && to_hint > to) ||
00290 to_hint == -1)
00291 to_hint = to;
00292 break;
00293 case CT_CURSOR:
00294 case (CT_JOINCUR | CT_CURSOR):
00295 to = *(ctp->ct_activep) + ctp->ct_timeout;
00296
00297 if (to < t) {
00298 if (__dbsrv_verbose)
00299 printf("Timing out cursor %ld\n",
00300 ctp->ct_id);
00301 (void)__dbc_close_int(ctp);
00302
00303
00304
00305 nextctp = LIST_FIRST(&__dbsrv_head);
00306 } else if ((to_hint > 0 && to_hint > to) ||
00307 to_hint == -1)
00308 to_hint = to;
00309 break;
00310 default:
00311 break;
00312 }
00313 }
00314
00315
00316
00317
00318 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; ctp = nextctp) {
00319 nextctp = LIST_NEXT(ctp, entries);
00320 if (ctp->ct_type != CT_ENV)
00321 continue;
00322 to = *(ctp->ct_activep) + ctp->ct_idle;
00323
00324 if (to < t || force) {
00325 if (__dbsrv_verbose)
00326 printf("Timing out env id %ld\n", ctp->ct_id);
00327 (void)__env_close_int(ctp->ct_id, 0, 1);
00328
00329
00330
00331
00332
00333 nextctp = LIST_FIRST(&__dbsrv_head);
00334 }
00335 }
00336 }
00337
00338
00339
00340
00341
00342 static void
00343 __dbclear_child(parent)
00344 ct_entry *parent;
00345 {
00346 ct_entry *ctp, *nextctp;
00347
00348 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL;
00349 ctp = nextctp) {
00350 nextctp = LIST_NEXT(ctp, entries);
00351 if (ctp->ct_type == 0)
00352 continue;
00353 if (ctp->ct_parent == parent) {
00354 __dbclear_child(ctp);
00355
00356
00357
00358
00359
00360 nextctp = LIST_NEXT(ctp, entries);
00361 __dbclear_ctp(ctp);
00362 }
00363 }
00364 }
00365
00366
00367
00368
00369 void
00370 __dbclear_ctp(ctp)
00371 ct_entry *ctp;
00372 {
00373 LIST_REMOVE(ctp, entries);
00374 __os_free(NULL, ctp);
00375 }
00376
00377
00378
00379
00380 void
00381 __dbdel_ctp(parent)
00382 ct_entry *parent;
00383 {
00384 __dbclear_child(parent);
00385 __dbclear_ctp(parent);
00386 }
00387
00388
00389
00390
00391 ct_entry *
00392 new_ct_ent(errp)
00393 int *errp;
00394 {
00395 time_t t;
00396 ct_entry *ctp, *octp;
00397 int ret;
00398
00399 if ((ret = __os_malloc(NULL, sizeof(ct_entry), &ctp)) != 0) {
00400 *errp = ret;
00401 return (NULL);
00402 }
00403 memset(ctp, 0, sizeof(ct_entry));
00404
00405
00406
00407
00408
00409
00410
00411 if ((t = time(NULL)) == -1) {
00412 *errp = __os_get_errno();
00413 __os_free(NULL, ctp);
00414 return (NULL);
00415 }
00416 octp = LIST_FIRST(&__dbsrv_head);
00417 if (octp != NULL && octp->ct_id >= t)
00418 t = octp->ct_id + 1;
00419 ctp->ct_id = (long)t;
00420 ctp->ct_idle = __dbsrv_idleto;
00421 ctp->ct_activep = &ctp->ct_active;
00422 ctp->ct_origp = NULL;
00423 ctp->ct_refcount = 1;
00424
00425 LIST_INSERT_HEAD(&__dbsrv_head, ctp, entries);
00426 return (ctp);
00427 }
00428
00429
00430
00431
00432 ct_entry *
00433 get_tableent(id)
00434 long id;
00435 {
00436 ct_entry *ctp;
00437
00438 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL;
00439 ctp = LIST_NEXT(ctp, entries))
00440 if (ctp->ct_id == id)
00441 return (ctp);
00442 return (NULL);
00443 }
00444
00445
00446
00447
00448
00449 ct_entry *
00450 __dbsrv_sharedb(db_ctp, name, subdb, type, flags)
00451 ct_entry *db_ctp;
00452 const char *name, *subdb;
00453 DBTYPE type;
00454 u_int32_t flags;
00455 {
00456 ct_entry *ctp;
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469 if (flags & DB_SERVER_DBNOSHARE)
00470 return (NULL);
00471 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL;
00472 ctp = LIST_NEXT(ctp, entries)) {
00473
00474
00475
00476 if (ctp == db_ctp)
00477 continue;
00478 if (ctp->ct_type != CT_DB)
00479 continue;
00480 if (ctp->ct_envparent != db_ctp->ct_envparent)
00481 continue;
00482 if (type != DB_UNKNOWN && ctp->ct_dbdp.type != type)
00483 continue;
00484 if (ctp->ct_dbdp.dbflags != LF_ISSET(DB_SERVER_DBFLAGS))
00485 continue;
00486 if (db_ctp->ct_dbdp.setflags != 0 &&
00487 ctp->ct_dbdp.setflags != db_ctp->ct_dbdp.setflags)
00488 continue;
00489 if (name == NULL || ctp->ct_dbdp.db == NULL ||
00490 strcmp(name, ctp->ct_dbdp.db) != 0)
00491 continue;
00492 if (subdb != ctp->ct_dbdp.subdb &&
00493 (subdb == NULL || ctp->ct_dbdp.subdb == NULL ||
00494 strcmp(subdb, ctp->ct_dbdp.subdb) != 0))
00495 continue;
00496
00497
00498
00499 ctp->ct_refcount++;
00500 return (ctp);
00501 }
00502
00503 return (NULL);
00504 }
00505
00506
00507
00508
00509
00510 ct_entry *
00511 __dbsrv_shareenv(env_ctp, home, flags)
00512 ct_entry *env_ctp;
00513 home_entry *home;
00514 u_int32_t flags;
00515 {
00516 ct_entry *ctp;
00517
00518
00519
00520
00521
00522
00523
00524 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL;
00525 ctp = LIST_NEXT(ctp, entries)) {
00526
00527
00528
00529 if (ctp == env_ctp)
00530 continue;
00531 if (ctp->ct_type != CT_ENV)
00532 continue;
00533 if (ctp->ct_envdp.home != home)
00534 continue;
00535 if (ctp->ct_envdp.envflags != flags)
00536 continue;
00537 if (ctp->ct_envdp.onflags != env_ctp->ct_envdp.onflags)
00538 continue;
00539 if (ctp->ct_envdp.offflags != env_ctp->ct_envdp.offflags)
00540 continue;
00541
00542
00543
00544
00545
00546
00547 if (ctp->ct_timeout < env_ctp->ct_timeout)
00548 ctp->ct_timeout = env_ctp->ct_timeout;
00549 ctp->ct_refcount++;
00550 return (ctp);
00551 }
00552
00553 return (NULL);
00554 }
00555
00556
00557
00558
00559 void
00560 __dbsrv_active(ctp)
00561 ct_entry *ctp;
00562 {
00563 time_t t;
00564 ct_entry *envctp;
00565
00566 if (ctp == NULL)
00567 return;
00568 if ((t = time(NULL)) == -1)
00569 return;
00570 *(ctp->ct_activep) = t;
00571 if ((envctp = ctp->ct_envparent) == NULL)
00572 return;
00573 *(envctp->ct_activep) = t;
00574 return;
00575 }
00576
00577
00578
00579
00580 int
00581 __db_close_int(id, flags)
00582 long id;
00583 u_int32_t flags;
00584 {
00585 DB *dbp;
00586 int ret;
00587 ct_entry *ctp;
00588
00589 ret = 0;
00590 ctp = get_tableent(id);
00591 if (ctp == NULL)
00592 return (DB_NOSERVER_ID);
00593 DB_ASSERT(ctp->ct_type == CT_DB);
00594 if (__dbsrv_verbose && ctp->ct_refcount != 1)
00595 printf("Deref'ing dbp id %ld, refcount %d\n",
00596 id, ctp->ct_refcount);
00597 if (--ctp->ct_refcount != 0)
00598 return (ret);
00599 dbp = ctp->ct_dbp;
00600 if (__dbsrv_verbose)
00601 printf("Closing dbp id %ld\n", id);
00602
00603 ret = dbp->close(dbp, flags);
00604 if (ctp->ct_dbdp.db != NULL)
00605 __os_free(NULL, ctp->ct_dbdp.db);
00606 if (ctp->ct_dbdp.subdb != NULL)
00607 __os_free(NULL, ctp->ct_dbdp.subdb);
00608 __dbdel_ctp(ctp);
00609 return (ret);
00610 }
00611
00612
00613
00614
00615 int
00616 __dbc_close_int(dbc_ctp)
00617 ct_entry *dbc_ctp;
00618 {
00619 DBC *dbc;
00620 int ret;
00621 ct_entry *ctp;
00622
00623 dbc = (DBC *)dbc_ctp->ct_anyp;
00624
00625 ret = dbc->c_close(dbc);
00626
00627
00628
00629
00630 if (dbc_ctp->ct_type & CT_JOINCUR)
00631 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL;
00632 ctp = LIST_NEXT(ctp, entries)) {
00633
00634
00635
00636
00637 if ((ctp->ct_type & CT_JOIN) &&
00638 ctp->ct_activep == &dbc_ctp->ct_active) {
00639 ctp->ct_type &= ~CT_JOIN;
00640 ctp->ct_activep = ctp->ct_origp;
00641 __dbsrv_active(ctp);
00642 }
00643 }
00644 __dbclear_ctp(dbc_ctp);
00645 return (ret);
00646
00647 }
00648
00649
00650
00651
00652 int
00653 __env_close_int(id, flags, force)
00654 long id;
00655 u_int32_t flags;
00656 int force;
00657 {
00658 DB_ENV *dbenv;
00659 int ret;
00660 ct_entry *ctp, *dbctp, *nextctp;
00661
00662 ret = 0;
00663 ctp = get_tableent(id);
00664 if (ctp == NULL)
00665 return (DB_NOSERVER_ID);
00666 DB_ASSERT(ctp->ct_type == CT_ENV);
00667 if (__dbsrv_verbose && ctp->ct_refcount != 1)
00668 printf("Deref'ing env id %ld, refcount %d\n",
00669 id, ctp->ct_refcount);
00670
00671
00672
00673
00674 if (--ctp->ct_refcount != 0 && !force)
00675 return (ret);
00676 dbenv = ctp->ct_envp;
00677 if (__dbsrv_verbose)
00678 printf("Closing env id %ld\n", id);
00679
00680
00681
00682
00683
00684
00685 if (force)
00686 for (dbctp = LIST_FIRST(&__dbsrv_head);
00687 dbctp != NULL; dbctp = nextctp) {
00688 nextctp = LIST_NEXT(dbctp, entries);
00689 if (dbctp->ct_type != CT_DB)
00690 continue;
00691 if (dbctp->ct_envparent != ctp)
00692 continue;
00693
00694
00695
00696
00697 __db_close_int(dbctp->ct_id, 0);
00698
00699
00700
00701
00702
00703 nextctp = LIST_FIRST(&__dbsrv_head);
00704 }
00705 ret = dbenv->close(dbenv, flags);
00706 __dbdel_ctp(ctp);
00707 return (ret);
00708 }
00709
00710 static int
00711 add_home(home)
00712 char *home;
00713 {
00714 home_entry *hp, *homep;
00715 int ret;
00716
00717 if ((ret = __os_malloc(NULL, sizeof(home_entry), &hp)) != 0)
00718 return (ret);
00719 if ((ret = __os_malloc(NULL, strlen(home)+1, &hp->home)) != 0)
00720 return (ret);
00721 memcpy(hp->home, home, strlen(home)+1);
00722 hp->dir = home;
00723 hp->passwd = NULL;
00724
00725
00726
00727
00728 hp->name = __db_rpath(home);
00729 if (hp->name != NULL) {
00730 *(hp->name) = '\0';
00731 hp->name++;
00732 } else
00733 hp->name = home;
00734 while (*(hp->name) == '\0') {
00735 hp->name = __db_rpath(home);
00736 *(hp->name) = '\0';
00737 hp->name++;
00738 }
00739
00740
00741
00742
00743 for (homep = LIST_FIRST(&__dbsrv_home); homep != NULL;
00744 homep = LIST_NEXT(homep, entries))
00745 if (strcmp(homep->name, hp->name) == 0) {
00746 printf("Already added home name %s, at directory %s\n",
00747 hp->name, homep->dir);
00748 __os_free(NULL, hp->home);
00749 __os_free(NULL, hp);
00750 return (-1);
00751 }
00752 LIST_INSERT_HEAD(&__dbsrv_home, hp, entries);
00753 if (__dbsrv_verbose)
00754 printf("Added home %s in dir %s\n", hp->name, hp->dir);
00755 return (0);
00756 }
00757
00758 static int
00759 add_passwd(passwd)
00760 char *passwd;
00761 {
00762 home_entry *hp;
00763
00764
00765
00766
00767
00768
00769 hp = LIST_FIRST(&__dbsrv_home);
00770 if (hp == NULL || hp->passwd != NULL)
00771 return (EINVAL);
00772
00773
00774
00775
00776 hp->passwd = passwd;
00777 return (0);
00778 }
00779
00780
00781
00782
00783 home_entry *
00784 get_fullhome(name)
00785 char *name;
00786 {
00787 home_entry *hp;
00788
00789 if (name == NULL)
00790 return (NULL);
00791 for (hp = LIST_FIRST(&__dbsrv_home); hp != NULL;
00792 hp = LIST_NEXT(hp, entries))
00793 if (strcmp(name, hp->name) == 0)
00794 return (hp);
00795 return (NULL);
00796 }
00797
00798 static int
00799 env_recover(progname)
00800 char *progname;
00801 {
00802 DB_ENV *dbenv;
00803 home_entry *hp;
00804 u_int32_t flags;
00805 int exitval, ret;
00806
00807 for (hp = LIST_FIRST(&__dbsrv_home); hp != NULL;
00808 hp = LIST_NEXT(hp, entries)) {
00809 exitval = 0;
00810 if ((ret = db_env_create(&dbenv, 0)) != 0) {
00811 fprintf(stderr, "%s: db_env_create: %s\n",
00812 progname, db_strerror(ret));
00813 exit(EXIT_FAILURE);
00814 }
00815 if (__dbsrv_verbose == 1)
00816 (void)dbenv->set_verbose(dbenv, DB_VERB_RECOVERY, 1);
00817 dbenv->set_errfile(dbenv, stderr);
00818 dbenv->set_errpfx(dbenv, progname);
00819 if (hp->passwd != NULL)
00820 (void)dbenv->set_encrypt(dbenv, hp->passwd,
00821 DB_ENCRYPT_AES);
00822
00823
00824
00825
00826
00827 if (__dbsrv_verbose)
00828 printf("Running recovery on %s\n", hp->home);
00829 flags = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
00830 DB_INIT_TXN | DB_USE_ENVIRON | DB_RECOVER;
00831 if ((ret = dbenv->open(dbenv, hp->home, flags, 0)) != 0) {
00832 dbenv->err(dbenv, ret, "DB_ENV->open");
00833 goto error;
00834 }
00835
00836 if (0) {
00837 error: exitval = 1;
00838 }
00839 if ((ret = dbenv->close(dbenv, 0)) != 0) {
00840 exitval = 1;
00841 fprintf(stderr, "%s: dbenv->close: %s\n",
00842 progname, db_strerror(ret));
00843 }
00844 if (exitval)
00845 return (exitval);
00846 }
00847 return (0);
00848 }