00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #if __linux__
00029 #include <stdint.h>
00030 #endif
00031 #include <sys/types.h>
00032 #include <dirent.h>
00033
00034 extern "C" int _Preaddir_r(DIR *, struct dirent *, struct dirent **);
00035
00036 #include <pthread.h>
00037 #include <time.h>
00038 #include <stdlib.h>
00039 #include <sys/stat.h>
00040 #include <ctype.h>
00041
00042 #include <VestaConfig.H>
00043 #include <Thread.H>
00044
00045 #include "ShortIdImpl.H"
00046 #include "VestaLog.H"
00047 #include "Recovery.H"
00048 #include "ReadersWritersLock.H"
00049 #include "ShortIdKey.H"
00050 #include "FdCache.H"
00051 #include "VRConcurrency.H"
00052 #include "logging.H"
00053
00054 #include "lock_timing.H"
00055
00056 using std::fstream;
00057 using std::hex;
00058 using std::dec;
00059
00060
00061
00062
00063 #define USE_RANDOM_R 1
00064
00065
00066 struct ShortIdBlockInfo {
00067 time_t leaseExpires;
00068 };
00069 typedef Table<ShortIdKey, ShortIdBlockInfo*>::Default BlockInfoTable;
00070 typedef Table<ShortIdKey, ShortIdBlockInfo*>::Iterator BlockInfoIter;
00071
00072
00073 const int LEASE_PERIOD = 60*60*24;
00074 const int LANDLORD_SLEEP = 60*60;
00075 const int LANDLORD_WORKLIST_SIZE = 128;
00076
00077
00078 static BlockInfoTable biTable;
00079 #if USE_RANDOM_R
00080 struct random_data randDat;
00081 int randState[32];
00082 #endif
00083 static Basics::mutex mu;
00084 static Basics::thread landlord;
00085 static Text sid_dir;
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095 void
00096 AcquireShortIdBlock(ShortIdBlock& bk, bool leafflag, bool local) throw()
00097 {
00098 ShortId start;
00099 int used, used2 = -1;
00100 ShortIdBlock bk2;
00101
00102 if (!local) {
00103 StableLock.acquireWrite();
00104 RWLOCK_LOCKED_REASON(&StableLock, "AcquireShortIdBlock");
00105 }
00106 mu.lock();
00107 for (;;) {
00108
00109 int randint;
00110 #if USE_RANDOM_R
00111 # if __digital__
00112 (void) random_r(&randint, &randDat);
00113 # else
00114 (void) random_r(&randDat, &randint);
00115 # endif
00116 #else
00117 randint = random();
00118 #endif
00119 start = (ShortId)
00120 ( (randint & ~(ShortIdBlock::size - 1) &
00121 ~ShortIdBlock::leafFlag & ~ShortIdBlock::dirFlag)
00122 | (leafflag ? ShortIdBlock::leafFlag : 0) );
00123 if (start == NullShortId) {
00124 continue;
00125 }
00126 ShortIdBlockInfo* dummy;
00127 if (biTable.Get(start, dummy)) {
00128
00129 continue;
00130 }
00131
00132
00133 bk.init(start);
00134 used = 0;
00135 char *name = ShortIdBlock::shortIdToName(start);
00136 char *p = strrchr(name, PathnameSep);
00137 assert(p != 0);
00138 *p = '\0';
00139 DIR *dir = opendir(name);
00140 delete[] name;
00141 if (!dir) {
00142
00143 break;
00144 }
00145 struct dirent de, *done;
00146 while (readdir_r(dir, &de, &done) == 0 &&
00147 done != (struct dirent *)NULL) {
00148 char *endptr;
00149 long offset = strtol(de.d_name, &endptr, 16);
00150 if (*endptr != '\0') continue;
00151 bk.set(start + offset);
00152 used++;
00153 }
00154 closedir(dir);
00155 if (used >= ShortIdBlock::size) {
00156
00157 continue;
00158 }
00159 if (used2 == -1 && used > ShortIdBlock::size / 2) {
00160
00161 bk2 = bk;
00162 used2 = used;
00163 continue;
00164 } else {
00165 break;
00166 }
00167 }
00168 if (used2 != -1) {
00169 if (used2 < used) {
00170
00171 bk = bk2;
00172 }
00173 }
00174 ShortIdKey key(start);
00175 ShortIdBlockInfo* bi = NEW(ShortIdBlockInfo);
00176 bk.leaseExpires = bi->leaseExpires =
00177 (local ? ShortIdBlock::leaseNonexpiring : time(NULL) + LEASE_PERIOD);
00178 biTable.Put(key, bi);
00179 if (!local) {
00180 char logrec[256];
00181
00182 int sres = sprintf(logrec, "(asidb 0x%x %d)\n",
00183 start, bk.leaseExpires);
00184 assert(sres > 0);
00185 assert(sres < sizeof(logrec));
00186 VRLog.start();
00187 VRLog.put(logrec);
00188 VRLog.commit();
00189 }
00190 mu.unlock();
00191 if (!local) {
00192 StableLock.releaseWrite();
00193 }
00194 }
00195
00196
00197
00198
00199
00200
00201
00202
00203 bool
00204 RenewShortIdBlock(ShortIdBlock& bk) throw()
00205 {
00206 StableLock.acquireWrite();
00207 RWLOCK_LOCKED_REASON(&StableLock, "RenewShortIdBlock");
00208 mu.lock();
00209 ShortIdKey key(bk.start);
00210 ShortIdBlockInfo* bi;
00211 bool inTable = biTable.Delete(key, bi);
00212 time_t now = time(0);
00213 if (!inTable || bi->leaseExpires < now) {
00214 mu.unlock();
00215 StableLock.releaseWrite();
00216 if(inTable) delete bi;
00217 return false;
00218 }
00219 bk.leaseExpires = bi->leaseExpires = now + LEASE_PERIOD;
00220 biTable.Put(key, bi);
00221 char logrec[256];
00222
00223 int sres = sprintf(logrec, "(asidb 0x%x %d)\n",
00224 bk.start, bk.leaseExpires);
00225 assert(sres > 0);
00226 assert(sres < sizeof(logrec));
00227 VRLog.start();
00228 VRLog.put(logrec);
00229 VRLog.commit();
00230 mu.unlock();
00231 StableLock.releaseWrite();
00232 return true;
00233 }
00234
00235
00236 static void
00237 ReleaseShortIdBlockAt(ShortId start, bool local) throw()
00238 {
00239 ShortIdBlockInfo* bi;
00240 bool ok = biTable.Delete(start, bi);
00241 if (ok) delete bi;
00242 if (!local) {
00243 char logrec[256];
00244
00245 int sres = sprintf(logrec, "(rsidb 0x%x)\n", start);
00246 assert(sres > 0);
00247 assert(sres < sizeof(logrec));
00248 VRLog.start();
00249 VRLog.put(logrec);
00250 VRLog.commit();
00251 }
00252 }
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262 void
00263 ReleaseShortIdBlock(ShortIdBlock& bk, bool local) throw()
00264 {
00265 if (!local) {
00266 StableLock.acquireWrite();
00267 RWLOCK_LOCKED_REASON(&StableLock, "ReleaseShortIdBlock");
00268 }
00269 mu.lock();
00270 ReleaseShortIdBlockAt(bk.start, local);
00271 mu.unlock();
00272 if (!local) {
00273 StableLock.releaseWrite();
00274 }
00275 }
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291 void *
00292 LandlordThread(void *arg)
00293 {
00294 signal(SIGPIPE, SIG_IGN);
00295 signal(SIGQUIT, SIG_DFL);
00296 signal(SIGSEGV, SIG_DFL);
00297 signal(SIGABRT, SIG_DFL);
00298 signal(SIGILL, SIG_DFL);
00299 signal(SIGBUS, SIG_DFL);
00300
00301 for (;;) {
00302 ShortId worklist[LANDLORD_WORKLIST_SIZE];
00303 int i = 0;
00304 StableLock.acquireWrite();
00305 RWLOCK_LOCKED_REASON(&StableLock, "LandlordThread");
00306 mu.lock();
00307 {
00308 BlockInfoIter iter(&biTable);
00309 ShortIdKey key;
00310 ShortIdBlockInfo* bi;
00311 time_t now = time(NULL);
00312 while (iter.Next(key, bi)) {
00313 if (bi->leaseExpires < now) {
00314 worklist[i++] = key.sid;
00315 if (i >= LANDLORD_WORKLIST_SIZE) break;
00316 }
00317 }
00318 }
00319 int j;
00320 for (j=0; j<i; j++) {
00321 ReleaseShortIdBlockAt(worklist[j], false);
00322 }
00323 mu.unlock();
00324 StableLock.releaseWrite();
00325 sleep(LANDLORD_SLEEP);
00326 }
00327
00328
00329 }
00330
00331
00332 static void
00333 AsidbCallback(RecoveryReader* rr, char &c)
00334 throw(VestaLog::Error, VestaLog::Eof)
00335 {
00336 long ltmp;
00337 ShortId start;
00338 time_t leaseExpires;
00339 RecoveryReader::Ident id;
00340
00341
00342 rr->getLong(c, ltmp);
00343 start = (ShortId) ltmp;
00344 rr->getLong(c, ltmp);
00345 leaseExpires = (ShortId) ltmp;
00346
00347 ShortIdKey key(start);
00348
00349 ShortIdBlockInfo* bi;
00350 bool inTable = biTable.Delete(key, bi);
00351 if(!inTable)
00352 bi = NEW(ShortIdBlockInfo);
00353 else
00354 assert(bi != 0);
00355 bi->leaseExpires = leaseExpires;
00356 mu.lock();
00357 inTable = biTable.Put(key, bi);
00358 assert(!inTable);
00359 mu.unlock();
00360 }
00361
00362 static void
00363 RsidbCallback(RecoveryReader* rr, char &c)
00364 throw(VestaLog::Error, VestaLog::Eof)
00365 {
00366 ShortId start;
00367 long lstart;
00368
00369
00370 rr->getLong(c, lstart);
00371 start = (ShortId) lstart;
00372 mu.lock();
00373 ShortIdBlockInfo* bi;
00374 bool ok = biTable.Delete(start, bi);
00375 if (ok) delete bi;
00376 mu.unlock();
00377 }
00378
00379
00380 void
00381 ShortIdBlockCheckpoint(fstream& ckpt) throw()
00382 {
00383
00384 mu.lock();
00385 BlockInfoIter iter(&biTable);
00386 ShortIdKey key;
00387 ShortIdBlockInfo* bi;
00388
00389 while (iter.Next(key, bi)) {
00390 ckpt << "(asidb 0x" << hex << key.sid << dec
00391 << " " << bi->leaseExpires << ")\n";
00392 }
00393
00394 mu.unlock();
00395 }
00396
00397
00398 static const int charsPerArc[] = { 3, 3, 2, 0 };
00399 static const int MAX_CHARS_PER_ARC = 3;
00400
00401 extern "C"
00402 {
00403 static int
00404 arccmp(const void* a, const void* b)
00405 {
00406 return strcmp((char*) a, (char*) b);
00407 }
00408 }
00409
00410 static ShortId
00411 NextSidToKeep(SourceOrDerived& sidstream)
00412 {
00413 ShortId nextkeep;
00414
00415 if (sidstream.eof()) {
00416 Repos::dprintf(DBG_SIDDEL, "at end of ShortIdsFile\n");
00417 nextkeep = NullShortId;
00418 } else {
00419 for (;;) {
00420 sidstream >> nextkeep;
00421 if (sidstream.fail()) {
00422 assert(sidstream.eof());
00423 Repos::dprintf(DBG_SIDDEL, "hit end of ShortIdsFile\n");
00424 nextkeep = NullShortId;
00425 } else if (nextkeep == NullShortId) {
00426 Repos::dprintf(DBG_ALWAYS, "error: NullShortId on keep list\n");
00427 continue;
00428 }
00429 Repos::dprintf(DBG_SIDDEL, "next to keep: 0x%08x\n", nextkeep);
00430 break;
00431 }
00432 }
00433 return nextkeep;
00434 }
00435
00436
00437
00438
00439 static int
00440 ScanDirForDelete(int levelnum, const char* dirname, Bit32 dirnum,
00441 ShortId& nextkeep, SourceOrDerived& sidstream, time_t lease)
00442 {
00443 typedef char SidArc[MAX_CHARS_PER_ARC + 1];
00444 SidArc *arcs = NEW_PTRFREE_ARRAY(SidArc, 1 << (MAX_CHARS_PER_ARC * 4));
00445 int narcs = 0;
00446 int i, nkept, ret;
00447 DIR* dir = opendir(dirname);
00448 if (dir == NULL) {
00449 Repos::dprintf(DBG_ALWAYS, "error opening sid directory, errno %d\n", errno);
00450 assert(errno != 0);
00451 return -errno;
00452 }
00453 struct dirent de, *done;
00454 while (readdir_r(dir, &de, &done) == 0 &&
00455 done != (struct dirent *)NULL) {
00456 bool ok = true;
00457 for (i = 0; i < charsPerArc[levelnum]; i++) {
00458 if (!isxdigit(de.d_name[i]) || isupper(de.d_name[i])) {
00459 ok = false;
00460 break;
00461 }
00462 }
00463 if (!ok || de.d_name[charsPerArc[levelnum]] != '\000') continue;
00464 strcpy(arcs[narcs++], de.d_name);
00465 }
00466 closedir(dir);
00467 if (narcs == 0) return 0;
00468 qsort(arcs, narcs, MAX_CHARS_PER_ARC + 1, arccmp);
00469 nkept = narcs;
00470 if (charsPerArc[levelnum + 1] == 0) {
00471
00472 for (i = 0; i < narcs; i++) {
00473 ShortId cursid = (dirnum << (charsPerArc[levelnum]*4)) +
00474 strtoul(arcs[i], NULL, 16);
00475 for (;;) {
00476 if (nextkeep != NullShortId && cursid > nextkeep) {
00477 if (SourceOrDerived::dirShortId(nextkeep)) {
00478 Repos::dprintf(DBG_SIDDISP, "directory: 0x%08x\n", nextkeep);
00479 } else {
00480 Repos::dprintf(DBG_ALWAYS, "missing: 0x%08x\n", nextkeep);
00481 }
00482 nextkeep = NextSidToKeep(sidstream);
00483 continue;
00484 }
00485 break;
00486 }
00487 if (nextkeep == cursid) {
00488 Repos::dprintf(DBG_SIDDISP, "listed: 0x%08x\n", cursid);
00489 nextkeep = NextSidToKeep(sidstream);
00490 } else {
00491
00492 char *sidname = NEW_PTRFREE_ARRAY(char, (strlen(dirname) + 1 +
00493 strlen(arcs[i]) + 1));
00494 if (*dirname == '\0') {
00495 strcpy(sidname, arcs[i]);
00496 } else {
00497 sprintf(sidname, "%s%c%s",
00498 dirname, PathnameSep, arcs[i]);
00499 }
00500 struct stat statbuf;
00501 int res = stat(sidname, &statbuf);
00502 if (res < 0) {
00503 if (errno == ENOENT) {
00504
00505 nkept--;
00506 delete [] sidname;
00507 continue;
00508 }
00509 Repos::dprintf(DBG_ALWAYS,
00510 "error on stat of sid, errno %d\n", errno);
00511 assert(errno != 0);
00512 delete [] sidname;
00513 return -errno;
00514 }
00515 if (statbuf.st_ctime < lease) {
00516 Repos::dprintf(DBG_SIDDISP, "garbage: 0x%08x\n", cursid);
00517 if (!Repos::isDebugLevel(DBG_SIDNODEL)) {
00518 res = unlink(sidname);
00519 if (res < 0 && errno != ENOENT) {
00520 Repos::dprintf(DBG_ALWAYS,
00521 "error on unlink of sid, errno %d\n", errno);
00522 assert(errno != 0);
00523 delete [] sidname;
00524 return -errno;
00525 }
00526 }
00527 FdCache::flush(cursid, FdCache::any);
00528 nkept--;
00529 } else {
00530 Repos::dprintf(DBG_SIDDISP, "leased: 0x%08x\n", cursid);
00531 }
00532 delete [] sidname;
00533 }
00534 }
00535 } else {
00536 for (i = 0; i < narcs; i++) {
00537 char *subdirname = NEW_PTRFREE_ARRAY(char, (strlen(dirname) + 1 +
00538 strlen(arcs[i]) + 1));
00539 Bit32 subdirnum = (dirnum << (charsPerArc[levelnum]*4)) +
00540 strtoul(arcs[i], NULL, 16);
00541 if (*dirname == '\0') {
00542 strcpy(subdirname, arcs[i]);
00543 } else {
00544 sprintf(subdirname, "%s%c%s",
00545 dirname, PathnameSep, arcs[i]);
00546 }
00547 ret = ScanDirForDelete(levelnum + 1, subdirname, subdirnum,
00548 nextkeep, sidstream, lease);
00549 if (ret < 0) {
00550 delete [] subdirname;
00551 return ret;
00552 }
00553 if (ret == 0) {
00554 Repos::dprintf(DBG_SIDDISP, "empty: %s\n", subdirname);
00555 if (!Repos::isDebugLevel(DBG_SIDNODEL)) {
00556 int res = rmdir(subdirname);
00557 if (res < 0) {
00558 Repos::dprintf(DBG_ALWAYS, "error on sid rmdir, errno %d\n",
00559 errno);
00560 assert(errno != 0);
00561 delete [] subdirname;
00562 return -errno;
00563 }
00564 }
00565 nkept--;
00566 }
00567 delete [] subdirname;
00568 }
00569 }
00570 delete [] arcs;
00571 return nkept;
00572 }
00573
00574
00575
00576
00577 int
00578 DeleteAllShortIdsBut(ShortIdsFile sidfile, time_t lease) throw()
00579 {
00580 ShortId nextkeep;
00581 SourceOrDerived sidstream;
00582 sidstream.open(sidfile);
00583 sidstream >> hex;
00584 if (!sidstream.good()) {
00585 int ret = errno;
00586 Repos::dprintf(DBG_ALWAYS,
00587 "error opening combined keepSidFile, errno %d\n", ret);
00588 assert(ret != 0);
00589 sidstream.close();
00590 return ret;
00591 }
00592
00593 nextkeep = NextSidToKeep(sidstream);
00594 int ret =
00595 ScanDirForDelete(0, sid_dir.cchars(), 0, nextkeep, sidstream, lease);
00596 sidstream.close();
00597 if (ret < 0) return -ret;
00598 return 0;
00599 }
00600
00601
00602 void
00603 ShortIdServerInit()
00604 {
00605 #if USE_RANDOM_R
00606 memset((void *) randState, 0, sizeof(randState));
00607 randDat.state = NULL;
00608 char *dummy;
00609 # if __digital__
00610 (void) initstate_r((unsigned) time(0),
00611 (char *) randState, sizeof(randState),
00612 &dummy, &randDat);
00613 # else
00614 (void) initstate_r((unsigned) time(0),
00615 (char *) randState, sizeof(randState),
00616 &randDat);
00617 # endif
00618 #else
00619 srandom((unsigned) time(NULL));
00620 #endif //NOTDEF
00621 RegisterRecoveryCallback("asidb", AsidbCallback);
00622 RegisterRecoveryCallback("rsidb", RsidbCallback);
00623 try {
00624 bool ok = VestaConfig::get("Repository", "sid_dir", sid_dir);
00625 if (!ok) sid_dir = "";
00626 } catch (VestaConfig::failure f) {
00627 Repos::dprintf(DBG_ALWAYS, "VestaConfig::failure %s\n", f.msg.cchars());
00628 abort();
00629 }
00630 }
00631
00632 void
00633 ShortIdServerInit2()
00634 {
00635 Basics::thread_attr landlord_attr;
00636 #if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && !defined(__linux__)
00637
00638 landlord_attr.set_schedpolicy(SCHED_RR);
00639 landlord_attr.set_inheritsched(PTHREAD_EXPLICIT_SCHED);
00640 landlord_attr.set_sched_priority(sched_get_priority_min(SCHED_RR));
00641 #endif
00642
00643 landlord.fork(LandlordThread, 0, landlord_attr);
00644 }