00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <dirent.h>
00022 #include <time.h>
00023 extern "C" {
00024
00025 struct tm *_Plocaltime_r(const time_t *timer, struct tm *result);
00026 }
00027 #include <Basics.H>
00028 #include <VestaConfig.H>
00029 #include <SourceOrDerived.H>
00030 #include <ParCacheC.H>
00031 #include <Timer.H>
00032 #include <ReposUI.H>
00033 #include "VASTi.H"
00034 #include "Lex.H"
00035 #include "Parser.H"
00036 #include "Expr.H"
00037 #include "Val.H"
00038 #include "PrimRunTool.H"
00039 #include "ToolDirectoryServer.H"
00040 #include "Location.H"
00041 #include "Err.H"
00042 #include "Files.H"
00043 #include "ApplyCache.H"
00044 #include "ThreadData.H"
00045
00046 using std::ios;
00047 using std::ostream;
00048 using std::fstream;
00049 using std::cout;
00050 using std::cerr;
00051 using std::endl;
00052 using std::flush;
00053
00054
00055 #if !defined(ESUCCESS)
00056 #define ESUCCESS 0
00057 #endif
00058
00059 static Text modelPath;
00060 static mode_t newUMask = 022, oldUMask;
00061
00062
00063
00064
00065 static bool callStackPrinted = false;
00066
00067 void PrintCacheStat(ostream *vout) {
00068 *vout << "\nCaching Stats:\n";
00069 *vout << " Cache: [" << "Server=" << *ParCacheC::Locate() << ", "
00070 << "MetaData=" << VestaConfig::get_Text("CacheServer", "MetaDataRoot")
00071 << "]\n";
00072 *vout << " Function: [" << "Calls=" << appCallCounter << ", "
00073 << "Hits=" << appHitCounter << "]\n";
00074 *vout << " Model: ["
00075 << "Calls=[" << "Special=" << sModelCallCounter << ", "
00076 << "Normal=" << nModelCallCounter << "], "
00077 << "Hits=[" << "Special=" << sModelHitCounter << ", "
00078 << "Normal=" << nModelHitCounter << "]" << "]\n";
00079 *vout << " RunTool: [" << "Calls=" << toolCallCounter << ", "
00080 << "Hits=" << toolHitCounter << "]\n";
00081 }
00082
00083 void PrintFuncTrace(ostream *vout) {
00084 if (recordTrace) {
00085 *vout << "\nFunction call graph:\n"
00086 << ThreadDataGet()->traceRes->str();
00087 }
00088 }
00089
00090 void PrintErrorStack(ostream *vout) {
00091 if (!recordCallStack) return;
00092 callStackMu.lock();
00093 if (callStackPrinted) {
00094 callStackMu.unlock();
00095 return;
00096 }
00097 callStackPrinted = true;
00098
00099 ThreadData *thdata = ThreadDataGet();
00100 *vout << "\nError stack trace:\n";
00101 int counter = 0;
00102 int st = 0;
00103 for (;;) {
00104 int sz = thdata->callStack->size();
00105 for (int i = st; i < sz; i++) {
00106 Expr expr = thdata->callStack->get(i);
00107 *vout << counter++ << ". " << expr->loc->file << ": line "
00108 << expr->loc->line << ", char " << expr->loc->character
00109 << endl;
00110 }
00111 if (thdata->parent == NULL) break;
00112 st = thdata->parent->callStack->size() - thdata->parentCallStackSize;
00113 thdata = thdata->parent;
00114 }
00115 callStackMu.unlock();
00116 }
00117
00118 AssocVC* Lookup(Val val) {
00119
00120 Val result = val;
00121 const char *path = shipFromPath.cchars();
00122 int len = shipFromPath.Length();
00123
00124 Text arc;
00125 int i = 0;
00126 while (i < len) {
00127 int j = i;
00128 while (j < len && !IsDelimiter(path[j])) j++;
00129 arc = shipFromPath.Sub(i, j);
00130 if (result->vKind == BindingVK) {
00131 AssocVC *as = FindInContext(arc, ((BindingVC*)result)->elems);
00132 if (as == nullAssoc) {
00133 cerr << "Warning: the path `" << shipFromPath
00134 << "' does not exist in the result value.\n";
00135 return as;
00136 }
00137 result = as->val;
00138 i = j + 1;
00139 }
00140 else {
00141 Error(Text("Failed to find the value to ship in ") +
00142 shipFromPath + ".\n");
00143 return NULL;
00144 }
00145 }
00146 return NEW_CONSTR(AssocVC, (arc, result));
00147 }
00148
00149 int Copy(const char *fromPath, const char *toPath) {
00150
00151
00152 const int BuffSz = 8192;
00153 char buf[BuffSz];
00154 int fromFd = open(fromPath, O_RDONLY);
00155 if (fromFd == -1) return errno;
00156
00157
00158
00159
00160 mode_t to_mode = (S_IRUSR | S_IWUSR |
00161 S_IRGRP | S_IWGRP |
00162 S_IROTH | S_IWOTH);
00163
00164
00165 struct stat from_stat;
00166 if(fstat(fromFd, &from_stat) == 0)
00167 {
00168 if(from_stat.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
00169 {
00170 to_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
00171 }
00172 }
00173 else
00174 {
00175
00176
00177 int err = errno;
00178 cerr << "Warning: couldn't stat file to be shipped (`" << fromPath
00179 << "'): " << strerror(err) << endl;
00180
00181 }
00182
00183 remove(toPath);
00184 int toFd = creat(toPath, to_mode);
00185 if (toFd == -1) {
00186 close(fromFd);
00187 return errno;
00188 }
00189
00190 int size;
00191 int eno = ESUCCESS;
00192 while (size = read(fromFd, buf, BuffSz)) {
00193 if (write(toFd, buf, size) == -1) {
00194 eno = errno;
00195 break;
00196 }
00197 }
00198 close(fromFd);
00199 close(toFd);
00200 return eno;
00201 }
00202
00203 int CleanDir(Text path) {
00204 int err = 0;
00205 int entries = 0;
00206
00207
00208 struct stat statbuf;
00209 bool havelog = (stat(".log", &statbuf) == 0);
00210
00211 DIR *dirPt = opendir(".");
00212 if (dirPt == NULL) {
00213 Error(Text("Failed to open the directory ") + path + ".\n");
00214 return -1;
00215 }
00216
00217 for (dirent *dp = readdir(dirPt); dp != NULL; dp = readdir(dirPt)) {
00218 if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..") &&
00219 strcmp(dp->d_name, ".log")) {
00220 entries++;
00221 if (!forceClean && !havelog) continue;
00222 err = lstat(dp->d_name, &statbuf);
00223 if (S_ISDIR(statbuf.st_mode)) {
00224
00225 err = chdir(dp->d_name);
00226 if (err) return err;
00227 err = CleanDir(path + dp->d_name + "/");
00228 if (err) return err;
00229 err = chdir("..");
00230 if (err) return err;
00231
00232 if(!hushedShipping)
00233 cout << "Cleaning " << path << dp->d_name << endl;
00234 err = rmdir(dp->d_name);
00235 if (err) return err;
00236
00237 } else {
00238
00239 if(!hushedShipping)
00240 cout << "Cleaning " << path << dp->d_name << endl;
00241 err = remove(dp->d_name);
00242 if (err) return err;
00243 }
00244 }
00245 }
00246 if (havelog) {
00247 err = remove(".log");
00248 if (err) return err;
00249 }
00250 (void)closedir(dirPt);
00251
00252 if (!forceClean && !havelog && entries > 0) {
00253 Error(Text("Tried to clean a nonempty directory with no .log file: ")
00254 + shipToPath + ".\n");
00255 return -1;
00256 }
00257
00258 return 0;
00259 }
00260
00261 enum ShipKind { CopyShip, SymLinkShip };
00262 const char *KindName[] = { "copy", "link" };
00263
00264
00265
00266 void LogShip(ShortId sid, const char *path,
00267 fstream &logFile, struct tm *shipTM, bool is_dir = false) throw ()
00268 {
00269 char dateStr[20];
00270 sprintf(dateStr, "%02d:%02d:%02d %02d-%02d-%04d",
00271 shipTM->tm_hour, shipTM->tm_min, shipTM->tm_sec,
00272 (shipTM->tm_mon + 1), shipTM->tm_mday, (1900 + shipTM->tm_year));
00273
00274 logFile << modelPath << ": " << path;
00275 if(is_dir) {
00276 logFile << " -> directory";
00277 }
00278 else {
00279 if (sid) {
00280 char sidHex[20];
00281 int err = sprintf(sidHex, "0x%08x", sid); assert(err >= 0);
00282 logFile << " -> " << sidHex;
00283 } else {
00284 logFile << " -> literal";
00285 }
00286 }
00287
00288 logFile << ' ' << dateStr << endl;
00289 logFile.flush();
00290 }
00291
00292 int ShipShortId(ShortId sid, const Text &name, ShipKind kind,
00293 fstream &logFile, struct tm *shipTM)
00294 throw () {
00295 int err = ESUCCESS;
00296 if (sid != NullShortId) {
00297 if (!hushedShipping)
00298 cout << "Shipping " << name << "..." << endl;
00299 char *path = SourceOrDerived::shortIdToName(sid, false);
00300 switch (kind) {
00301 case CopyShip:
00302 err = Copy(path, name.cchars());
00303 break;
00304 case SymLinkShip:
00305 remove(name.cchars());
00306 if (symlink(path, name.cchars()) == -1) {
00307 err = errno;
00308 }
00309 break;
00310 default:
00311 assert(false);
00312 }
00313 if (err != ESUCCESS) {
00314 Error(Text("Failed to ") + KindName[(int)kind] + " the file " +
00315 name + ": " + strerror(err) + ".\n");
00316 }
00317 else {
00318 LogShip(sid, name.cchars(), logFile, shipTM);
00319 }
00320 }
00321 return err;
00322 }
00323
00324 int ShipText(const Text &txt, const Text &name,
00325 fstream &logFile, struct tm *shipTM)
00326 throw () {
00327 if (!hushedShipping)
00328 cout << "Shipping " << name << "..." << endl;
00329 remove(name.cchars());
00330 fstream outFile;
00331 outFile.open(name.cchars(), ios::out);
00332 if (outFile.fail()) {
00333 Error(Text("Failed to create the file ") + name +
00334 ": " + strerror(errno) + ".\n");
00335 return errno;
00336 }
00337 outFile << txt << flush;
00338 if (outFile.fail()) {
00339 Error(Text("Failed to write to the file ") + name +
00340 ": " + strerror(errno) + ".\n");
00341 return errno;
00342 }
00343 outFile.close();
00344 LogShip(0, name.cchars(), logFile, shipTM);
00345 return 0;
00346 }
00347
00348 int ShipValue(Val value, ShipKind kind, struct tm *shipTM) throw () {
00349 fstream logFile;
00350 logFile.open(dotLogFiles?".log":"/dev/null",
00351 ios::out | ios::app);
00352 if (logFile.fail()) {
00353 Error(Text("Failed to open .log file: ") + strerror(errno) + ".\n");
00354 return -1;
00355 }
00356
00357
00358 if (value->vKind == BindingVK) {
00359 BindingVC *bv = (BindingVC*)value;
00360 Context work = bv->elems;
00361 while (!work.Null()) {
00362 Assoc elem = work.Pop();
00363 Val val = elem->val;
00364 ShortId sid;
00365 int err = 0;
00366 switch (val->vKind) {
00367 case TextVK:
00368 if (((TextVC*)val)->HasSid()) {
00369 sid = ((TextVC*)val)->Sid();
00370 err = ShipShortId(sid, elem->name, kind, logFile, shipTM);
00371 }
00372 else {
00373 err = ShipText(((TextVC*)val)->NDS(), elem->name, logFile, shipTM);
00374 }
00375 if (err) return err;
00376 break;
00377 case ModelVK:
00378 sid = ((ModelVC*)val)->Sid();
00379 err = ShipShortId(sid, elem->name, kind, logFile, shipTM);
00380 if (err) return err;
00381 break;
00382 case BindingVK:
00383 {
00384 if(!FS::IsDirectory(elem->name.cchars())) {
00385
00386 err = mkdir(elem->name.cchars(), 0xfff);
00387 if (err) {
00388 Error(Text("Failed to create the directory ") +
00389 elem->name + ": " + strerror(errno) + ".\n");
00390 }
00391 }
00392 LogShip(0, elem->name.cchars(), logFile, shipTM, true);
00393 }
00394 if (err) return err;
00395 err = chdir(elem->name.cchars());
00396 if (err) return err;
00397 err = ShipValue(val, kind, shipTM);
00398 if (err) return err;
00399 err = chdir("..");
00400 if (err) return err;
00401 break;
00402 default:
00403
00404 break;
00405 }
00406 }
00407 }
00408 logFile.close();
00409 return 0;
00410 }
00411
00412 bool VestaShip(AssocVC *namedVal) {
00413
00414
00415 int err = 0;
00416 const char *path = shipToPath.cchars();
00417 Val shipValue;
00418 Text log_dir = shipToPath;
00419
00420 err = chdir(path);
00421 if (!err) {
00422 if (shipClean || forceClean) {
00423
00424 if(hushedShipping) {
00425 cout << "Cleaning..." << endl;
00426 cout.flush();
00427 }
00428 Text dirPath = shipToPath;
00429 if(dirPath[dirPath.Length()-1] != '/')
00430 dirPath += "/";
00431 err = CleanDir(dirPath);
00432 if (err) {
00433 Error(Text("Failed to delete the contents of the directory ") +
00434 shipToPath + ".\n");
00435 return false;
00436 }
00437 }
00438 if (namedVal->val->vKind == BindingVK) {
00439 shipValue = namedVal->val;
00440 }
00441 else {
00442 shipValue = NEW_CONSTR(BindingVC, (Context(namedVal)));
00443 }
00444 }
00445 else if ((errno == ENOTDIR || errno == ENOENT) &&
00446 (namedVal->val->vKind == TextVK ||
00447 namedVal->val->vKind == ModelVK)) {
00448
00449 Text filename;
00450 try {
00451 FS::SplitPath(shipToPath, filename, log_dir);
00452 }
00453 catch(FS::Failure f) {
00454 Error(Text("FS failure: ") + f.get_op() + ": " + f.get_errno() + ".\n");
00455 return false;
00456 }
00457 err = chdir(log_dir.cchars());
00458 if(!err) {
00459 shipValue =
00460 NEW_CONSTR(BindingVC,
00461 (Context(NEW_CONSTR(AssocVC, (filename, namedVal->val)))));
00462 }
00463 }
00464
00465 if(err) {
00466 Error(Text("Failed to cd to the directory ") + log_dir + ": "
00467 + strerror(errno) + ".\n");
00468 return false;
00469 }
00470
00471
00472 if (hushedShipping) {
00473 cout << "Shipping...\n";
00474 cout.flush();
00475 }
00476 time_t nowT;
00477 if (time(&nowT) == (time_t)(-1)) {
00478 Error(Text("Getting time of day from time(): ") + strerror(errno) + ".\n");
00479 return false;
00480 }
00481 else {
00482 struct tm buffTM, *nowTM;
00483 nowTM = localtime_r(&nowT, &buffTM);
00484 ShipKind kind = (shipBySymLink ? SymLinkShip : CopyShip);
00485
00486
00487
00488 err = ShipValue(shipValue, kind, nowTM);
00489 }
00490
00491 if (hushedShipping) {
00492 cout << "Done!\n";
00493 cout.flush();
00494 }
00495 return (err == 0);
00496 }
00497
00498 bool Interpret(const Text& model) {
00499 Text prefix;
00500 VestaSource *vSource;
00501 fstream *iFile;
00502 VestaSource::errorCode err;
00503 Expr expr;
00504 Val result;
00505 bool success = true;
00506
00507 uid_t euid = geteuid();
00508 if (seteuid(getuid()) < 0) {
00509 throw(Evaluator::failure(Text("Failed to switch to the real user-id.\n"),
00510 false));
00511 }
00512
00513 try {
00514 modelPath = ReposUI::canonicalize(model);
00515 }
00516 catch(ReposUI::failure f) {
00517 Error(Text("ReposUI failure: ") + f.msg + ".\n");
00518 return false;
00519 }
00520
00521 if (seteuid(euid) < 0) {
00522 throw(Evaluator::failure(
00523 Text("Failed to switch back to the effective user-id.\n"),
00524 false));
00525 }
00526 if (!OpenSource(NULL, modelPath, noLoc, iFile, topModelRoot,
00527 topModelSid, vSource)) {
00528
00529 success = false;
00530 }
00531 else {
00532
00533 if (iFile == NULL) {
00534 iFile = NEW(SourceOrDerived);
00535 ((SourceOrDerived*)iFile)->open(topModelSid);
00536 if (iFile->bad()) {
00537 throw(Evaluator::failure(Text("Can't open the model ") + modelPath +
00538 "; giving up!\n",
00539 false));
00540 }
00541 }
00542 try {
00543 expr = Parse(iFile, modelPath, topModelSid, topModelRoot);
00544 } catch (const char* report) {
00545
00546 success = false;
00547 }
00548 iFile->close();
00549 }
00550 if (!success) {
00551 Error("Failed to parse `" + model + "'.\n\n");
00552 return false;
00553 }
00554 if (parseOnly) {
00555 cout << "Parsing completed.\n";
00556 return true;
00557 }
00558
00559
00560 ModelVC *fun = NULL;
00561 try {
00562 ModelEC *modelExpr = (ModelEC*)expr;
00563 Context cc = ProcessModelHead(modelExpr);
00564 fun = NEW_CONSTR(ModelVC, (modelPath, topModelSid, topModelRoot,
00565 modelExpr, cc, vSource));
00566 ArgList args = NEW_CONSTR(ArgListEC, (0, modelExpr->loc));
00567 ApplyEC *ae = NEW_CONSTR(ApplyEC, (NEW_CONSTR(NameEC, (model, noLoc)),
00568 args, modelExpr->loc));
00569 if (recordCallStack) {
00570 ThreadDataGet()->callStack->addlo(modelExpr);
00571 }
00572 result = ApplyModel(fun, ae, conInitial);
00573 } catch (SRPC::failure f) {
00574 Error(Text("SRPC failure (") + IntToText(f.r) + "): " + f.msg + ".\n");
00575 success = false;
00576 } catch (Evaluator::failure f) {
00577
00578 success = false;
00579 if (f.r) {
00580
00581
00582 success = StartEval(model);
00583 exit(success ? 0 : 1);
00584 }
00585 } catch (const char* report) {
00586
00587 success = false;
00588 } catch (ReposUI::failure f) {
00589 Error(Text("ReposUI failure: ") + f.msg + ".\n");
00590 success = false;
00591 } catch (VestaConfig::failure f) {
00592 Error(Text("VestaConfig failure: ") + f.msg + ".\n");
00593 success = false;
00594 }
00595
00596
00597 if (success && printResult) {
00598
00599
00600 cout << "\nReturn value of `" << model << "':\n";
00601 result->PrintD(&cout);
00602 cout << "\n";
00603 }
00604
00605
00606 if (ErrorCount() > 0) success = false;
00607
00608
00609
00610 if (!success) PrintErrorStack(&cerr);
00611
00612
00613 PrintFuncTrace(&cout);
00614
00615
00616 if (cacheOption != 0) {
00617 if (printCacheStats) PrintCacheStat(&cout);
00618
00619
00620 CacheEntry::IndicesApp* orphanCIs = ThreadDataGet()->orphanCIs;
00621 if (fun != NULL && orphanCIs->len > 0)
00622 try {
00623 theCache->Checkpoint(topModelRoot->fptag, topModelSid, *orphanCIs, true);
00624 } catch (SRPC::failure f) {
00625 Error(Text("SRPC failure when checkpointing cache (")
00626 + IntToText(f.r) + "): " + f.msg + ".\n");
00627 success = false;
00628 }
00629 }
00630
00631
00632
00633
00634 if (success && !shipToPath.Empty()) {
00635 AssocVC *shipValue = Lookup(result);
00636 if (shipValue != NULL) {
00637 if (seteuid(getuid()) < 0) {
00638 Error("Failed to switch to the real user-id.\n");
00639 success = false;
00640 }
00641 else {
00642 umask(oldUMask);
00643 success = VestaShip(shipValue);
00644 }
00645 }
00646 }
00647
00648
00649 if (psStat) {
00650 cout << "\nEvaluator Process Stats:\n ";
00651 cout.flush();
00652 Text command("ps -o pid,user,vsz,rss,time,comm -A | grep ");
00653 command += IntToText(getpid());
00654 system(command.chars());
00655 }
00656
00657
00658 cout.flush();
00659 return success;
00660 }
00661
00662 bool StartEval(const Text& filename) {
00663
00664 ThreadDataInit();
00665 ErrInit();
00666 LexInit();
00667 ValInit();
00668 PrimInit();
00669 PrimRunToolInit();
00670 StartRenewLeaseThread();
00671
00672
00673
00674
00675 try {
00676 newUMask = 022;
00677 newUMask = VestaConfig::get_int("Repository", "umask");
00678 } catch (VestaConfig::failure) {
00679
00680 }
00681 oldUMask = umask(newUMask);
00682
00683
00684 return Interpret(filename);
00685 }